remove unused card templates and related files
This commit is contained in:
@@ -31,7 +31,6 @@
|
||||
<div style="margin-top:0.5rem">
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Speed <small class="is-size-7">(km/h)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.speed_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div>{{ card.speed_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">
|
||||
@@ -42,7 +41,6 @@
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Weight <small class="is-size-7">(kg)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.weight_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div>{{ card.weight_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">
|
||||
@@ -53,7 +51,6 @@
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Intelligence <small class="is-size-7">(score)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.intelligence_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div class="is-size-7" style="font-weight:600">
|
||||
{% for t in card.intelligence_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<div style="margin-top:0.8rem">
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Speed <small class="is-size-7">(km/h)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.speed_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:700">
|
||||
<div>{{ card.speed_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">{% for t in card.speed_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %} <span style="margin-left:6px">(orig: {{ card.speed_orig }})</span></div>
|
||||
@@ -39,7 +38,6 @@
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Weight <small class="is-size-7">(kg)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.weight_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:700">
|
||||
<div>{{ card.weight_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">{% for t in card.weight_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %} <span style="margin-left:6px">(orig: {{ card.weight_orig }})</span></div>
|
||||
@@ -47,7 +45,6 @@
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Intelligence <small class="is-size-7">(score)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.intelligence_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:700">
|
||||
<div class="is-size-7" style="font-weight:600">{% for t in card.intelligence_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}</div>
|
||||
<div class="is-size-7" style="font-weight:500">(orig: {{ card.intelligence_orig }})</div>
|
||||
@@ -55,7 +52,6 @@
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Height <small class="is-size-7">(m)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.height_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:700">
|
||||
<div>{{ card.height_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">{% for t in card.height_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %} <span style="margin-left:6px">(orig: {{ card.height_orig }})</span></div>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<article class="card-item" id="card-{{ card.pk }}">
|
||||
<div class="toptrump-card">
|
||||
<div class="hero-image" style="background-image: url('{% if card.background and card.background.image %}{{ card.background.image.url }}{% elif background and background.image %}{{ background.image.url }}{% endif %}');">
|
||||
{% if card.image %}
|
||||
<img class="card-hero-img" src="{{ card.image.url }}" alt="{{ card.name }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="title-band">{{ card.name }}</div>
|
||||
<div class="card-body">
|
||||
<div class="subtitle is-7"><strong>Era:</strong> {{ card.get_era_display }} — <strong>Diet:</strong> {{ card.get_diet_display }}</div>
|
||||
{% if card.facts_list %}
|
||||
<ul class="fact-list is-size-7">
|
||||
{% for fact in card.facts_list|slice:":2" %}
|
||||
<li>{{ fact }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<div style="margin-top:0.5rem">
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Speed <small class="is-size-7">(km/h)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.speed_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div>{{ card.speed_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">
|
||||
{% for t in card.speed_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}
|
||||
<span style="margin-left:6px">(orig: {{ card.speed_orig }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Weight <small class="is-size-7">(kg)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.weight_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div>{{ card.weight_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">
|
||||
{% for t in card.weight_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}
|
||||
<span style="margin-left:6px">(orig: {{ card.weight_orig }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Height <small class="is-size-7">(m)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.height_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div>{{ card.height_display }}</div>
|
||||
<div class="is-size-7" style="font-weight:500">
|
||||
{% for t in card.height_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}
|
||||
<span style="margin-left:6px">(orig: {{ card.height_orig }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-label">Intelligence <small class="is-size-7">(score)</small></div>
|
||||
<div class="stat-bar"><div class="stat-fill" style="width:{{ card.intelligence_percent }}%"></div></div>
|
||||
<div class="stat-value" style="margin-left:8px;font-weight:600">
|
||||
<div class="is-size-7" style="font-weight:600">
|
||||
{% for t in card.intelligence_star_types %}<span class="star {{ t }}">{% if t == 'full' %}★{% else %}☆{% endif %}</span>{% endfor %}
|
||||
</div>
|
||||
<div class="is-size-7" style="font-weight:500">(orig: {{ card.intelligence_orig }})</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons" style="margin-top:0.6rem">
|
||||
<button class="button is-small is-info" hx-get="{% url 'cards:edit' card.pk %}" hx-target="#htmx-form" hx-swap="innerHTML">Edit</button>
|
||||
<button class="button is-small is-primary" hx-get="{% url 'cards:preview' card.pk %}" hx-target="#modal-root" hx-swap="innerHTML">Preview</button>
|
||||
<button class="button is-small is-danger" hx-post="{% url 'cards:delete' card.pk %}" hx-confirm="Delete this card?" hx-swap="none">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -1,10 +0,0 @@
|
||||
{% load static %}
|
||||
<div class="columns is-multiline">
|
||||
{% for card in cards %}
|
||||
<div class="column is-3-desktop is-4-tablet is-6-mobile">
|
||||
{% include 'cards/_card_item.html' with card=card background=background %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="notification is-warning">No dinosaurs yet — add one!</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -1,193 +0,0 @@
|
||||
<div class="card-form card">
|
||||
{% if card %}
|
||||
<form method="post" enctype="multipart/form-data" action="{% url 'cards:edit' card.pk %}" hx-post="{% url 'cards:edit' card.pk %}" hx-target="#card-{{ card.pk }}" hx-swap="outerHTML">
|
||||
{% else %}
|
||||
<form method="post" enctype="multipart/form-data" action="{% url 'cards:create' %}" hx-post="{% url 'cards:create' %}" hx-target="#cards-list .columns" hx-swap="afterbegin">
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div class="field">
|
||||
<label class="label">{{ form.name.label }}</label>
|
||||
<div class="control">
|
||||
{{ form.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label">{{ form.era.label }}</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">{{ form.era }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ form.diet.label }}</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">{{ form.diet }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ form.description.label }}</label>
|
||||
<div class="control">
|
||||
{{ form.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ form.facts.label }}</label>
|
||||
<div class="control">
|
||||
{{ form.facts }}
|
||||
<p class="help">One fact per line — will be shown as bullet points on the card.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ form.image.label }}</label>
|
||||
<div class="control">
|
||||
<div class="file has-name">
|
||||
<label class="file-label">
|
||||
{{ form.image }}
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">📁</span>
|
||||
<span class="file-label">Choose a file…</span>
|
||||
</span>
|
||||
<span class="file-name" style="margin-left:0.5rem">No file chosen</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ form.background.label }}</label>
|
||||
<div class="control">
|
||||
<div style="display:none">{{ form.background }}</div>
|
||||
<div class="background-picker">
|
||||
<div class="bg-options">
|
||||
<div class="bg-option" data-bg-id="" role="button" tabindex="0">Use site default</div>
|
||||
{% for bg in backgrounds %}
|
||||
<div class="bg-option{% if form.instance.background and form.instance.background.pk == bg.pk %} selected{% endif %}" data-bg-id="{{ bg.pk }}" title="{{ bg.title|default:'Background' }}">
|
||||
<img src="{{ bg.image.url }}" alt="{{ bg.title }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="help">Optional: choose a background image to use for this card.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
const script = document.currentScript;
|
||||
const root = script && script.parentElement ? script.parentElement : document;
|
||||
const picker = root.querySelector('.background-picker');
|
||||
if(!picker) return;
|
||||
const select = root.querySelector('select[name="background"]');
|
||||
const options = picker.querySelectorAll('.bg-option');
|
||||
function clearSelected(){ options.forEach(o=>o.classList.remove('selected')); }
|
||||
options.forEach(function(opt){
|
||||
opt.addEventListener('click', function(){
|
||||
const id = opt.getAttribute('data-bg-id');
|
||||
clearSelected();
|
||||
opt.classList.add('selected');
|
||||
if(select){ select.value = id || '';
|
||||
// dispatch change for any listeners
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
(function(){
|
||||
const script = document.currentScript;
|
||||
const formRoot = script && script.parentElement ? script.parentElement : document;
|
||||
const fileInputs = formRoot.querySelectorAll('input[type=file]');
|
||||
fileInputs.forEach(function(fi){
|
||||
fi.addEventListener('change', function(){
|
||||
const wrapper = fi.closest('.file');
|
||||
if(!wrapper) return;
|
||||
const label = wrapper.querySelector('.file-name');
|
||||
if(!label) return;
|
||||
if(fi.files.length === 0) label.textContent = 'No file chosen';
|
||||
else label.textContent = fi.files.length > 1 ? fi.files.length + ' files selected' : fi.files[0].name;
|
||||
try {
|
||||
const nameInput = formRoot.querySelector('input[name="name"]');
|
||||
if(nameInput && (!nameInput.value || nameInput.value.trim() === '')){
|
||||
const f = fi.files && fi.files[0];
|
||||
if(f && f.name){
|
||||
const base = f.name.replace(/\.[^/.]+$/, '');
|
||||
const title = base.replace(/[_-]+/g, ' ').split(/\s+/).map(function(w){ return w.length? (w.charAt(0).toUpperCase() + w.slice(1)) : ''; }).join(' ').trim();
|
||||
if(title) nameInput.value = title;
|
||||
}
|
||||
}
|
||||
}catch(e){ /* ignore UI enhancement errors */ }
|
||||
});
|
||||
const outerLabel = fi.closest('.file').querySelector('.file-label');
|
||||
if(outerLabel && !outerLabel.contains(fi)){
|
||||
outerLabel.addEventListener('click', function(e){ fi.click(); });
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label">{{ form.speed.label }}</label>
|
||||
<div class="control">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">{{ form.speed }}</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">km/h</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ form.weight.label }}</label>
|
||||
<div class="control">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">{{ form.weight }}</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">kg</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ form.intelligence.label }}</label>
|
||||
<div class="control">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">{{ form.intelligence }}</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">score</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ form.height.label }}</label>
|
||||
<div class="control">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">{{ form.height }}</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">m</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-light" type="button" onclick="document.getElementById('htmx-form').innerHTML=''">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,147 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>TopTrumps - Dinosaurs</title>
|
||||
<!-- Small inline favicon to avoid browser requesting /favicon.ico and spamming 404s -->
|
||||
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.5"></script>
|
||||
<style>
|
||||
/* Theme variables */
|
||||
:root{
|
||||
--bg:#ffffff;
|
||||
--text:#0a0a0a;
|
||||
--muted:#4a4a4a;
|
||||
--card-bg:#ffffff;
|
||||
--accent:#00d1b2;
|
||||
}
|
||||
[data-theme="dark"]{
|
||||
--bg:#0b0f13;
|
||||
--text:#e6eef3;
|
||||
--muted:#9aa8b0;
|
||||
--card-bg:#0f1620;
|
||||
--accent:#79ffd4;
|
||||
}
|
||||
|
||||
body{background:var(--bg); color:var(--text)}
|
||||
.app-container{padding:1.5rem}
|
||||
.card-item{min-height:140px; background:var(--card-bg); color:var(--text)}
|
||||
.highlight-new{animation: highlight 1.6s ease}
|
||||
@keyframes highlight{0%{background:lavenderblush}100%{background:transparent}}
|
||||
|
||||
/* Theme toggle */
|
||||
#theme-toggle{margin-left:0.5rem}
|
||||
/* Top Trumps card styles */
|
||||
.toptrump-card{max-width:320px;border-radius:10px;overflow:hidden;background:var(--card-bg);color:var(--text);box-shadow:0 6px 18px rgba(0,0,0,0.12);border:1px solid rgba(0,0,0,0.06)}
|
||||
.toptrump-card .hero-image{height:180px;background-size:cover;background-position:center;position:relative;overflow:hidden}
|
||||
/* Hero image: make the dinosaur image larger, cover the banner and remove shadow */
|
||||
.toptrump-card .hero-image img.card-hero-img{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform:translate(-50%,-50%);
|
||||
/* ensure the image fills the banner while keeping aspect ratio */
|
||||
min-width:110%;
|
||||
min-height:110%;
|
||||
width:auto;
|
||||
height:auto;
|
||||
object-fit:cover;
|
||||
border-radius:6px;
|
||||
box-shadow:none;
|
||||
}
|
||||
/* Background picker thumbnails */
|
||||
.background-picker{margin-top:0.5rem}
|
||||
.background-picker .bg-options{display:flex;gap:0.5rem;flex-wrap:wrap}
|
||||
.background-picker .bg-option{width:64px;height:48px;border:1px solid rgba(0,0,0,0.06);display:flex;align-items:center;justify-content:center;cursor:pointer;background:#fff;color:#222;padding:4px;border-radius:4px}
|
||||
.background-picker .bg-option img{max-width:100%;max-height:100%;display:block;border-radius:2px}
|
||||
.background-picker .bg-option.selected{outline:2px solid var(--accent);box-shadow:0 2px 6px rgba(0,0,0,0.08)}
|
||||
.background-picker .bg-option[role="button"]{font-size:0.8rem;padding:6px 8px}
|
||||
/* Bulk edit thumbnails */
|
||||
.bulk-thumb{width:48px;height:48px;object-fit:cover;border-radius:6px;border:1px solid rgba(0,0,0,0.06);display:inline-block}
|
||||
.bulk-thumb.empty{background:rgba(0,0,0,0.04);width:48px;height:48px;border-radius:6px}
|
||||
.toptrump-card .title-band{background:var(--accent);color:#072027;padding:0.6rem 0.75rem;text-align:center;font-weight:700;font-size:1.05rem}
|
||||
.toptrump-card .card-body{padding:0.75rem}
|
||||
.toptrump-card .stat-row{display:flex;align-items:center;gap:0.75rem;padding:0.25rem 0}
|
||||
.toptrump-card .stat-label{width:90px;font-weight:600}
|
||||
.toptrump-card .stat-bar{flex:1;height:12px;background:rgba(255,255,255,0.06);border-radius:8px;overflow:hidden}
|
||||
.toptrump-card .stat-fill{height:100%;background:linear-gradient(90deg,var(--accent),#ffdd57);}
|
||||
.toptrump-card .fact-list{margin-top:0.5rem;padding-left:1rem}
|
||||
/* Star rating visuals */
|
||||
.star { font-size:0.95rem; display:inline-block; line-height:1; margin-left:2px; }
|
||||
.star.full { color:#ffdd57; }
|
||||
.star.empty { color:rgba(255,255,255,0.25); }
|
||||
.star.half { color:rgba(255,255,255,0.25); position:relative; display:inline-block; }
|
||||
.star.half::before { content: '★'; color:#ffdd57; position:absolute; left:0; top:0; width:50%; overflow:hidden; display:inline-block; }
|
||||
.stat-value { text-align:right; min-width:6.5rem }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="section">
|
||||
<div class="container app-container">
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<h1 class="title">TopTrumps — Dinosaurs</h1>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<button id="theme-toggle" class="button is-small" aria-pressed="false">Toggle theme</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="messages"></div>
|
||||
{% block content %}{% endblock %}
|
||||
<div id="modal-root"></div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
<script>
|
||||
(function(){
|
||||
const root = document.documentElement;
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
function applyTheme(t){
|
||||
if(t==='dark') root.setAttribute('data-theme','dark');
|
||||
else root.removeAttribute('data-theme');
|
||||
if(toggle) toggle.setAttribute('aria-pressed', t==='dark');
|
||||
}
|
||||
// initial: localStorage -> prefers-color-scheme -> light
|
||||
const stored = localStorage.getItem('toptrumps-theme');
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
applyTheme(stored || (prefersDark ? 'dark' : 'light'));
|
||||
if(toggle){
|
||||
toggle.addEventListener('click', ()=>{
|
||||
const current = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
|
||||
const next = current === 'dark' ? 'light' : 'dark';
|
||||
applyTheme(next);
|
||||
localStorage.setItem('toptrumps-theme', next);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Ensure HTMX requests include Django CSRF token (reads from cookie)
|
||||
(function(){
|
||||
function getCookie(name){
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== ''){
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i=0;i<cookies.length;i++){
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')){
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
document.body.addEventListener('htmx:configRequest', function(evt){
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
if (csrftoken){
|
||||
evt.detail.headers['X-CSRFToken'] = csrftoken;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,134 +0,0 @@
|
||||
{% extends "cards/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<h2 class="title is-4">Cards overview — bulk edit</h2>
|
||||
<p class="subtitle is-6">Edit multiple cards inline and save changes in one go.</p>
|
||||
|
||||
<style>
|
||||
/* Allow inputs inside the table to wrap on narrow viewports */
|
||||
.table-container .field.has-addons{white-space:normal}
|
||||
.table-container .control.is-expanded{width:100%}
|
||||
.table-container input.input{min-width:50px}
|
||||
/* Ensure thumbnails don't shrink layout unexpectedly */
|
||||
.bulk-thumb{flex:none}
|
||||
/* Make this page full-bleed: allow the app container to use the full viewport width */
|
||||
.app-container{max-width:none; width:100%; padding-left:0.5rem; padding-right:0.5rem}
|
||||
/* Remove extra section padding so content touches edges if needed */
|
||||
section.section{padding-left:0; padding-right:0}
|
||||
/* Table container: allow horizontal scroll and make table use full available width */
|
||||
.table-container{width:100%; overflow-x:auto}
|
||||
.table-container .table{min-width:1100px}
|
||||
</style>
|
||||
|
||||
<div style="margin-bottom:1rem">
|
||||
<form method="post" action="{% url 'cards:overview_create_images' %}" enctype="multipart/form-data" style="display:inline-block; margin-right:1rem">
|
||||
{% csrf_token %}
|
||||
<div class="file has-name">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" name="images" multiple>
|
||||
<span class="file-cta"><span class="file-icon">📁</span><span class="file-label">Select images…</span></span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="button is-small is-link" type="submit">Create cards from images</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'cards:overview_bulk_update' %}">
|
||||
{% csrf_token %}
|
||||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Preview</th>
|
||||
<th>Name</th>
|
||||
<th>Background</th>
|
||||
<th>Speed</th>
|
||||
<th>Weight</th>
|
||||
<th>Height</th>
|
||||
<th>Intel</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if formset %}
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
{{ form.id }}
|
||||
<tr>
|
||||
<td>
|
||||
{% if form.instance.image %}
|
||||
<img class="bulk-thumb" src="{{ form.instance.image.url }}" alt="{{ form.instance.name }}">
|
||||
{% else %}
|
||||
<div class="bulk-thumb empty"></div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ form.name }}</td>
|
||||
<td>{{ form.background }}</td>
|
||||
<td style="width:90px">{{ form.speed }}</td>
|
||||
<td style="width:90px">{{ form.weight }}</td>
|
||||
<td style="width:90px">{{ form.height }}</td>
|
||||
<td style="width:90px">{{ form.intelligence }}</td>
|
||||
<td style="width:90px">ID: {{ form.instance.pk }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for card in cards %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if card.image %}
|
||||
<img class="bulk-thumb" src="{{ card.image.url }}" alt="{{ card.name }}">
|
||||
{% else %}
|
||||
<div class="bulk-thumb empty"></div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><input class="input" name="name-{{ card.pk }}" value="{{ card.name }}"></td>
|
||||
<td>
|
||||
<div class="select"><select name="background-{{ card.pk }}">
|
||||
<option value="">Site default</option>
|
||||
{% for bg in backgrounds %}
|
||||
<option value="{{ bg.pk }}"{% if card.background and card.background.pk == bg.pk %} selected{% endif %}>{{ bg.title|default:'Background' }}</option>
|
||||
{% endfor %}
|
||||
</select></div>
|
||||
</td>
|
||||
<td style="width:90px">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded"><input class="input" name="speed-{{ card.pk }}" value="{{ card.speed }}"></div>
|
||||
<div class="control"><a class="button is-static">km/h</a></div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:90px">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded"><input class="input" name="weight-{{ card.pk }}" value="{{ card.weight }}"></div>
|
||||
<div class="control"><a class="button is-static">kg</a></div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:90px">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded"><input class="input" name="height-{{ card.pk }}" value="{{ card.height }}"></div>
|
||||
<div class="control"><a class="button is-static">m</a></div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:90px">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded"><input class="input" name="intelligence-{{ card.pk }}" value="{{ card.intelligence }}"></div>
|
||||
<div class="control"><a class="button is-static">score</a></div>
|
||||
</div>
|
||||
</td>
|
||||
<td>ID: {{ card.pk }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Save changes</button>
|
||||
<a class="button is-light" href="{% url 'cards:list' %}">Back to list</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends 'cards/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<button class="button is-primary" hx-get="{% url 'cards:create' %}" hx-target="#htmx-form" hx-swap="innerHTML">Add Dinosaur</button>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<a class="button is-light" href="{% url 'cards:backgrounds' %}">Manage backgrounds</a>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<a class="button is-light" href="{% url 'cards:overview' %}">Bulk edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="htmx-form"></div>
|
||||
|
||||
<div id="cards-list">
|
||||
{% include 'cards/_cards_list.html' %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user