- /s/<id> shows a spool: rest in Spoolman and CC2 Control, where it is, last booking, history, links to Spoolman and the tag generator. - Setting "Adresse der Bridge"; the tag generator can put <address>/s/<id> on the tag, so an iPhone held to the roll opens that page. - New spool: vendor and material chosen from Spoolman, the vendor's filaments offered, values taken from similar filaments, duplicates pointed out and never created twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC
78 lines
4.3 KiB
HTML
78 lines
4.3 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}CC2 Bridge – Spule {{ spool_id }}{% endblock %}
|
||
{% block heading %}Spule{% endblock %}
|
||
{% block head %}
|
||
<style>
|
||
.wrap { max-width: 720px; }
|
||
.hero { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
|
||
.hero h2 { font-size: 26px; letter-spacing: -.02em; overflow-wrap: anywhere; }
|
||
.big { font-family: var(--mono); font-size: 28px; }
|
||
.where { font-size: 15px; padding: 6px 12px; }
|
||
.acts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
||
.acts .btn { min-height: 48px; }
|
||
.entry { display: flex; gap: 12px; padding: 12px 0; border-top: 1px solid var(--line2); }
|
||
.entry:first-child { border-top: 0; }
|
||
</style>
|
||
{% endblock %}
|
||
{% block content %}
|
||
<div class="wrap stack" id="page" data-id="{{ spool_id }}"><div class="card muted">Lade …</div></div>
|
||
{% endblock %}
|
||
{% block script %}
|
||
<script>
|
||
const ID = Number($('page').dataset.id);
|
||
|
||
function render(d) {
|
||
const s = d.spool, color = s.color ? '#' + s.color : 'var(--faint)';
|
||
const initial = s.initial || 1000, rest = s.remaining;
|
||
const pct = rest == null ? 0 : Math.max(0, Math.min(100, rest / initial * 100));
|
||
let where;
|
||
if (s.archived) where = '<span class="chip where">Archiviert (leer)</span>';
|
||
else if (d.slot && d.slot.takeout) where = `<span class="chip warn where">${esc(d.slot.printer)} · Slot ${d.slot.slot} (herausgenommen, steckt noch)</span>`;
|
||
else if (d.slot) where = `<span class="chip accent dot where">${esc(d.slot.printer)} · Slot ${d.slot.slot}</span>`;
|
||
else where = `<span class="chip where">${esc(s.location || d.storage)}</span>`;
|
||
const rows = [
|
||
['Rest in Spoolman', rest == null ? '–' : `${fmtNum(rest, 1)} g`],
|
||
...d.copies.map((c) => [`Rest in CC2 Control (${esc(c.printer)})`, c.remaining == null ? '–' : `${fmtNum(c.remaining, 1)} g`]),
|
||
['Ort in Spoolman', esc(s.location || '–')],
|
||
['Zuletzt gebucht', d.last_booking ? `${fmtNum(d.last_booking.grams, 2)} g · ${fmtTime(d.last_booking.time)}${d.last_booking.details ? `<br><span class="small muted">${esc(d.last_booking.details)}</span>` : ''}` : '–'],
|
||
['Material', `${esc(s.vendor)} ${esc(s.material)}${s.diameter ? ` · ${s.diameter} mm` : ''}${s.density ? ` · ${s.density} g/cm³` : ''}`],
|
||
['Düse', s.extruder_temp ? `${s.extruder_temp} °C` : '–'],
|
||
['Leergewicht', s.spool_weight ? `${fmtNum(s.spool_weight)} g` : '–'],
|
||
];
|
||
$('page').innerHTML = `
|
||
<div class="card stack">
|
||
<div class="hero">
|
||
<div class="ring" style="width:120px;height:120px;background:conic-gradient(${color} 0 ${pct}%, var(--track) ${pct}% 100%)" aria-hidden="true">
|
||
<div style="width:88px;height:88px"><span class="mono" style="font-size:20px;line-height:1">${rest == null ? '?' : Math.round(rest)}</span><span class="small muted">von ${Math.round(initial)} g</span></div>
|
||
</div>
|
||
<div style="flex:1;min-width:200px" class="stack">
|
||
<div><div class="small muted">Spoolman #${s.id}</div><h2>${esc(s.label)}</h2><div class="muted">${esc(d.filament_name)}</div></div>
|
||
<div>${where}</div>
|
||
</div>
|
||
</div>
|
||
<dl class="kv">${rows.map(([k, v]) => `<dt>${k}</dt><dd>${v}</dd>`).join('')}</dl>
|
||
<div class="acts">
|
||
<a class="btn" href="${esc(d.spoolman_url)}" target="_blank" rel="noopener">In Spoolman öffnen</a>
|
||
<a class="btn" href="/tags?spool=${s.id}">Tag neu schreiben</a>
|
||
</div>
|
||
</div>
|
||
${d.log.length ? `<div class="label section-label" style="margin:8px 4px 0">Verlauf</div>
|
||
<div class="group" style="padding:0 14px">${d.log.map((e) => {
|
||
const [ic, bg, fg] = logIcon(e), [pn, msg] = splitPrinter(e.msg, d.copies.map((c) => c.printer).concat(d.slot ? [d.slot.printer] : []));
|
||
return `<div class="entry"><div class="icon-badge" style="background:${bg};color:${fg}">${ic}</div>
|
||
<div style="min-width:0"><div style="font-size:14px;overflow-wrap:anywhere">${esc(msg)}</div><div class="small muted">${esc(pn)} · ${fmtTime(e.time)}</div></div></div>`;
|
||
}).join('')}</div>` : ''}`;
|
||
}
|
||
|
||
async function load() {
|
||
try {
|
||
const d = await api('GET', `/api/spool/${ID}`);
|
||
setMode(d.dry_run);
|
||
render(d);
|
||
} catch (e) { $('page').innerHTML = `<div class="banner err">${esc(e.message)}</div>`; }
|
||
}
|
||
load();
|
||
setInterval(() => { if (!document.hidden) load(); }, 10000);
|
||
</script>
|
||
{% endblock %}
|