Files
encounterflow/battleflow/templates/admin.html

236 lines
9.6 KiB
HTML
Raw Normal View History

2025-11-20 14:40:42 +01:00
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>✦ {{ name }} Admin</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<style>
body{ font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial; background:#0b0d12; color:#e9eef4; margin:0; }
.topbar{ display:flex; justify-content:space-between; align-items:flex-start; gap:12px; padding:10px 16px 0; }
.left{ display:flex; flex-direction:column; gap:6px; }
.title{ opacity:.65;font-size:12px }
.subtitle{ opacity:.55; font-size:11px; margin-top:-2px; }
.globalbar{ display:flex; gap:8px; flex-wrap:wrap; }
.seg{ display:inline-flex; gap:6px; align-items:center; }
.seg .btn{ padding:4px 8px; font-size:12px; }
.seg .on{ outline:2px solid #334155; }
.wrap{ display:grid; grid-template-columns: 520px 1fr; gap:16px; padding:16px; }
.card{ background:#121621; border:1px solid #1f2535; border-radius:12px; padding:14px; }
h2{ margin:0 0 10px; font-size:16px; letter-spacing:0.4px; }
label{ font-size:12px; color:#9aa0aa; }
input, select{ width:100%; padding:8px; border-radius:8px; border:1px solid #293145; background:#0d111a; color:#e9eef4; }
.row{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
table{ width:100%; border-collapse:collapse; }
th,td{ border-bottom:1px solid #1f2535; padding:8px; font-size:14px; vertical-align:top; }
tr.active{ outline:2px solid #fde047; }
button{ padding:6px 8px; border-radius:10px; border:1px solid #293145; background:#0d111a; color:#e9eef4; cursor:pointer; }
.btn{ padding:6px 10px; border-radius:10px; border:1px solid #293145; background:#0d111a; color:#e9eef4; cursor:pointer; }
.btn-danger{ border-color:#7f1d1d; background:#1a0b0b; color:#fecaca; }
.btn-outline{ background:transparent; }
.pill{ display:inline-block; padding:2px 8px; border-radius:999px; font-size:12px; border:1px solid #293145; }
.tagType{ display:inline-block; padding:2px 8px; border-radius:999px; font-size:12px; border:1px solid transparent; font-weight:600; }
.tagType.pc{ background:#082f35; color:#7dd3fc; border-color:#0c4a59; }
.tagType.npc{ background:#1d1533; color:#c4b5fd; border-color:#352a5f; }
.tagType.monster{ background:#3b1111; color:#fca5a5; border-color:#5b1a1a; }
.bar{ display:flex; gap:8px; margin-top:8px; flex-wrap: wrap; }
.condbar button{ margin:2px; font-size:12px; display:inline-flex; align-items:center; gap:6px; }
.condbar button.on{ background:#1e293b; border-color:#334155; }
.presetlist{ max-height: 260px; overflow:auto; border:1px solid #1f2535; border-radius:10px; padding:6px; }
.preset{ display:flex; align-items:center; gap:8px; padding:6px; border-bottom:1px dashed #1f2535; }
.preset:last-child{ border-bottom:0; }
.pav{ width:22px; height:22px; border-radius:6px; object-fit:cover; background:#0d111a; }
.uploadrow{ display:grid; grid-template-columns: 1fr auto auto 68px; gap:8px; align-items:center; }
.preview{ width:64px; height:64px; border-radius:10px; background:#0d111a; border:1px solid #293145; object-fit:cover; }
.dropzone{ border:2px dashed #293145; border-radius:10px; padding:12px; text-align:center; color:#9aa0aa; }
.dropzone.drag{ background:#0f172a; border-color:#334155; color:#cbd5e1; }
.icoimg{ width:14px; height:14px; display:inline-block; vertical-align:-2px; }
.credit{ font-size:11px; color:#94a3b8; opacity:0.8; margin: 16px; text-align:center; }
.twocol{ display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.scroll{ max-height:180px; overflow:auto; border:1px solid #1f2535; border-radius:10px; padding:8px; }
.muted{ color:#9aa0aa; font-size:12px; }
</style>
</head>
<body>
<div class="topbar">
<div class="left">
<div class="title">{{ name|upper }} ADMIN</div>
<div class="subtitle">{{ subtitle }}</div>
<div class="globalbar">
<button class="btn" onclick="clearAll()">Clear</button>
<button class="btn" onclick="toggleVisible()">Toggle Board</button>
<button class="btn" onclick="prev()">Prev</button>
<button class="btn" onclick="next()">Next</button>
<div class="seg" id="deadseg">
<span class="muted">Dead cards:</span>
<button class="btn" data-mode="normal" onclick="setDeadMode('normal')">Normal</button>
<button class="btn" data-mode="shrink" onclick="setDeadMode('shrink')">Shrink</button>
<button class="btn" data-mode="hide" onclick="setDeadMode('hide')">Hide</button>
</div>
</div>
</div>
<div style="display:flex; gap:8px; align-items:center;">
<form action="/admin/seed_icons?token={{ token }}" method="post" onsubmit="return confirm('Download curated icons from Game-Icons.net now?');" style="margin:0">
<button class="btn">Seed curated icon set</button>
</form>
<button class="btn" onclick="openBoard()">Open Player Board ↗</button>
</div>
</div>
<div class="wrap">
<div class="card">
<h2>Add Actor</h2>
<div class="row">
<div>
<label>Name</label>
<input id="name"/>
</div>
<div>
<label>Init</label>
<input id="init" type="number" step="0.1" />
</div>
</div>
<div class="row" style="margin-top:6px;">
<div>
<label>HP</label>
<input id="hp" type="number"/>
</div>
<div>
<label>AC</label>
<input id="ac" type="number"/>
</div>
</div>
<div class="row" style="margin-top:6px;">
<div>
<label>Type</label>
<select id="type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="monster">Monster</option>
</select>
</div>
<div>
<label>Note</label>
<input id="note"/>
</div>
</div>
<div class="row" style="margin-top:6px;align-items:center;grid-template-columns:1fr auto auto 68px;">
<div>
<label>Avatar (filename in /avatars or full URL)</label>
<input id="avatar" placeholder="goblin.png or https://..."/>
</div>
<div class="dropzone" id="avatar_drop">Drop image here</div>
<button class="btn" onclick="uploadAvatar('avatar_file','avatar','avatar_preview')">Upload</button>
<img id="avatar_preview" class="preview" alt="preview"/>
</div>
<div class="uploadrow" style="margin-top:6px;">
<input type="file" id="avatar_file" accept="image/*" />
<div class="muted">or drag & drop ↑</div>
<div></div>
<div></div>
</div>
<div class="bar">
<button class="btn" onclick="add()">Add</button>
</div>
</div>
<div class="card">
<h2>Order (desc by init) — Round <span id="round">1</span></h2>
<table>
<thead>
<tr><th>#</th><th>Name</th><th>Init</th><th>HP</th><th>AC</th><th>Type</th><th>Flags</th><th></th></tr>
</thead>
<tbody id="rows"></tbody>
</table>
</div>
</div>
<div class="wrap">
<div class="card">
<h2>Add Preset</h2>
<div class="row">
<div>
<label>Name</label>
<input id="p_name"/>
</div>
<div>
<label>Type</label>
<select id="p_type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="monster">Monster</option>
</select>
</div>
</div>
<div class="row" style="margin-top:6px;">
<div>
<label>HP</label>
<input id="p_hp" type="number"/>
</div>
<div>
<label>AC</label>
<input id="p_ac" type="number"/>
</div>
</div>
<div class="row" style="margin-top:6px;">
<div>
<label>Note</label>
<input id="p_note"/>
</div>
<div>
<label>Avatar</label>
<input id="p_avatar" placeholder="filename in /avatars or URL"/>
</div>
</div>
<div class="row" style="margin-top:6px;align-items:center;grid-template-columns:1fr auto auto 68px;">
<div class="dropzone" id="p_avatar_drop">Drop image here</div>
<button class="btn" onclick="uploadAvatar('p_avatar_file','p_avatar','p_avatar_preview')">Upload</button>
<img id="p_avatar_preview" class="preview" alt="preview"/>
<div></div>
</div>
<div class="uploadrow" style="margin-top:6px;">
<input type="file" id="p_avatar_file" accept="image/*" />
<div class="muted">or drag & drop ↑</div>
<div></div>
<div></div>
</div>
<div class="bar">
<button class="btn" onclick="presetAdd()">Save Preset</button>
</div>
</div>
<div class="card">
<h2>Presets</h2>
<div class="presetlist" id="presetlist"></div>
</div>
</div>
<div class="wrap">
<div class="card">
<h2>Create Preset Group (party)</h2>
<div class="twocol">
<div>
<label>Group name</label>
<input id="g_name" placeholder="Party Alpha" />
<div class="bar"><button class="btn" onclick="groupAdd()">Save Group</button></div>
</div>
<div>
<label>Select presets</label>
<div id="g_checks" class="scroll"></div>
</div>
</div>
</div>
<div class="card">
<h2>Preset Groups</h2>
<div class="presetlist" id="pg_list"></div>
</div>
</div>
<p class="credit">Icons live in <code>{{ data_dir }}/icons</code>. Curated set from <strong>Game-Icons.net</strong> (CCBY 3.0). Authors: Lorc, Delapouite, Skoll, sbed. Replace any icon by dropping an SVG with the same name (e.g., <code>poisoned.svg</code>).</p>
<script src="{{ url_for('static', filename='js/admin.js') }}"></script>
<script>
// initialise toggles/preview/dropzones and start polling
initAdmin('{{ token }}');
</script>
</body>
</html>