Files
eve_structure/webapp/templates/structures.html
2025-08-27 18:55:45 +02:00

538 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>EVE Web Helper Strukturen</title>
<style>
:root { color-scheme: light dark; }
html{scroll-behavior:smooth}
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:2rem}
.container{max-width:1440px;margin:0 auto}
/* Haupt-Tabs oben */
.nav{display:flex;gap:.6rem;margin-bottom:1rem}
.tab{padding:.5rem .8rem;border:1px solid #e5e7eb;border-radius:.6rem;text-decoration:none;color:#111827}
.tab.active{background:#111827;color:#fff;border-color:#111827}
/* Linke Unterreiter (untergeordnete Seite) */
.subtabs-left{
position:fixed; left:.8rem; top:96px; display:flex; flex-direction:column; gap:.5rem;
width:170px; padding:.6rem; border:1px solid #e5e7eb; border-radius:.6rem; background:#fff;
box-shadow:0 2px 8px rgba(0,0,0,.05); z-index:50
}
.subtabs-left a{display:block;text-decoration:none;padding:.5rem .6rem;border:1px solid #e5e7eb;border-radius:.6rem;color:#111827;background:#fff}
.subtabs-left a.active{background:#111827;color:#fff;border-color:#111827}
@media (max-width:1200px){.subtabs-left{display:none}}
.card{border:1px solid #e5e7eb;border-radius:.8rem;padding:1rem 1.2rem;margin-bottom:1rem;background:#fff}
.wide{max-width:1200px}
.full{width:100%}
h1{font-size:1.35rem;margin:.2rem 0 1rem}
label{font-weight:600;display:block;margin-bottom:.35rem}
select,input,textarea,button,a.button{padding:.5rem .7rem;border:1px solid #d1d5db;border-radius:.45rem}
textarea{width:100%;min-height:90px;resize:vertical}
button,a.button{background:#111827;color:#fff;cursor:pointer;text-decoration:none;display:inline-block}
.muted{color:#6b7280;font-size:.9rem}
.row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.8rem;margin-top:.8rem}
@media (max-width:1000px){.row{grid-template-columns:1fr 1fr}}
table{width:100%;border-collapse:collapse;table-layout:fixed}
th,td{padding:.5rem .45rem;border-bottom:1px solid #e5e7eb;text-align:left;vertical-align:top;word-break:break-word;overflow-wrap:anywhere}
th{font-weight:700}
.id{font-family:ui-monospace,SFMono-Regular,Consolas,Menlo,monospace;font-size:.85em;max-width:90px}
.num{text-align:right}
.actions form{display:inline}
.section-title{font-weight:700;margin-bottom:.6rem}
.empty{padding:.65rem .8rem;border:1px dashed #d1d5db;border-radius:.6rem;color:#6b7280;background:transparent}
.table-wrap{overflow:auto;border:1px solid #e5e7eb;border-radius:.6rem}
.table-wrap thead th{position:sticky;top:0;background:#fff;z-index:1}
.scroll-5{max-height:260px}
@media (max-width:1600px){ .col-notes{display:none} }
@media (max-width:1450px){ .col-react-rig{display:none} }
@media (max-width:1350px){ .col-ind-rig{display:none} }
@media (max-width:1250px){ .col-ind-struct,.col-react-struct{display:none} }
/* Modal */
.modal{position:fixed;inset:0;display:none;align-items:flex-start;justify-content:center;padding:4vh 1rem;background:rgba(0,0,0,.45);z-index:999}
.modal:target{display:flex}
.dialog{max-width:1100px;width:min(92vw,1100px);background:#fff;border-radius:14px;padding:1rem 1.2rem;box-shadow:0 20px 45px rgba(0,0,0,.35);max-height:92vh;overflow:auto;position:relative;z-index:1000;pointer-events:auto}
.modal *{pointer-events:auto}
.modal h2{margin:.2rem 0 1rem;font-size:1.2rem}
.close{position:sticky;top:0;float:right;font-size:1.4rem;text-decoration:none;color:#111827;padding:.2rem .5rem;border-radius:.4rem;z-index:2}
.kv{display:grid;grid-template-columns:180px 1fr;gap:.35rem .8rem}
hr.sep{border:none;border-top:1px solid #e5e7eb;margin:.8rem 0}
.pill{display:inline-block;background:#111827;color:#fff;border-radius:999px;padding:.2rem .55rem;font-size:.85rem}
.result{border:1px solid #e5e7eb;border-radius:.6rem;padding:.7rem .8rem;background:#fafafa;margin-top:.6rem}
.err{color:#b91c1c}
@media (prefers-color-scheme:dark){
body{color:#e5e7eb;background:#0b0f19}
.tab{border-color:#374151;color:#e5e7eb}
.tab.active{background:#1f2937;border-color:#374151}
.card,.dialog,.subtabs-left{background:#0f1423;border-color:#374151}
th,td{border-bottom-color:#374151}
.table-wrap{border-color:#374151}
.table-wrap thead th{background:#0f1423}
.result{background:#111318;border-color:#374151}
.close{color:#e5e7eb}
.subtabs-left a{color:#e5e7eb;border-color:#374151;background:#0f1423}
.subtabs-left a.active{background:#1f2937}
}
body{overflow-x:hidden}
</style>
</head>
<body>
<!-- Unterreiter links AKTIV-Status serverseitig über request.path -->
<nav class="subtabs-left" aria-label="Unterreiter">
<a href="/strukturen"
class="{{ 'active' if request.path == '/strukturen' else '' }}">Aufträge</a>
<a href="/strukturen/mineralien"
class="{{ 'active' if request.path.startswith('/strukturen/mineralien') else '' }}">Mineralien</a>
</nav>
<div class="container">
<nav class="nav">
<a class="tab" href="/">Home</a>
<a class="tab active" href="/strukturen">Strukturen</a>
<a class="tab" href="/archiv">Archiv</a>
</nav>
<!-- Formular -->
<section class="card wide">
<h1>Struktur-Auftrag anlegen</h1>
<form method="post">
<div class="row">
<div>
<label for="structure-select">Struktur</label>
<select id="structure-select" name="structure" required>
<option value="" disabled selected>— bitte wählen —</option>
{% for s in structures %}<option value="{{ s }}">{{ s }}</option>{% endfor %}
</select>
</div>
<div>
<label for="quantity">Menge</label>
<input id="quantity" name="quantity" type="number" min="1" step="1" value="{{ quantity or 1 }}" required />
</div>
<div>
<label for="me">ME&nbsp;%</label>
<input id="me" name="me" type="number" min="0" max="10" step="1" value="{{ me_percent or 0 }}" />
</div>
<div>
<label for="system">System</label>
<select id="system" name="system">
{% for sys in systems %}<option value="{{ sys }}" {{ 'selected' if selected_system == sys else '' }}>{{ sys }}</option>{% endfor %}
</select>
</div>
</div>
<div class="row">
<div>
<label for="industry_structure">Industrie-Struktur</label>
<select id="industry_structure" name="industry_structure">
{% for x in ind_structs %}<option value="{{ x }}" {{ 'selected' if selected_ind_struct == x else '' }}>{{ x }}</option>{% endfor %}
</select>
</div>
<div>
<label for="industry_rig">Industrie-Rig</label>
<select id="industry_rig" name="industry_rig">
{% for x in ind_rigs %}<option value="{{ x }}" {{ 'selected' if selected_ind_rig == x else '' }}>{{ x }}</option>{% endfor %}
</select>
</div>
<div>
<label for="reaction_structure">Reaktions-Struktur</label>
<select id="reaction_structure" name="reaction_structure">
{% for x in reac_structs %}<option value="{{ x }}" {{ 'selected' if selected_reac_struct == x else '' }}>{{ x }}</option>{% endfor %}
</select>
</div>
<div>
<label for="reaction_rig">Reaktions-Rig</label>
<select id="reaction_rig" name="reaction_rig">
{% for x in reac_rigs %}<option value="{{ x }}" {{ 'selected' if selected_reac_rig == x else '' }}>{{ x }}</option>{% endfor %}
</select>
</div>
</div>
<div class="row">
<div>
<label for="facility_tax">Steuersatz&nbsp;% (facilityTax)</label>
<input id="facility_tax" name="facility_tax" type="number" min="0" max="50" step="0.1" value="0" />
</div>
<div style="grid-column: span 3">
<label for="notes">Notizen</label>
<textarea id="notes" name="notes" placeholder="Optionale Notizen zum Auftrag (max. ~2000 Zeichen)"></textarea>
<div class="muted" style="margin-top:.4rem">ME in 1er Schritten (010). Steuersatz in Prozent.</div>
</div>
</div>
<button type="submit" style="margin-top:.8rem">Auftrag hinzufügen</button>
</form>
</section>
<!-- Offene Aufträge -->
<section class="card full">
<div class="section-title">Offene Aufträge</div>
{% if open_orders %}
<div class="table-wrap scroll-5">
<table aria-label="Offene Aufträge">
<thead>
<tr>
<th>ID</th>
<th>Struktur</th>
<th>System</th>
<th class="col-ind-struct">Ind.-Struktur</th>
<th class="col-ind-rig">Ind.-Rig</th>
<th class="col-react-struct">Reakt.-Struktur</th>
<th class="col-react-rig">Reakt.-Rig</th>
<th class="num">Menge</th>
<th class="num">ME&nbsp;%</th>
<th class="col-notes">Notizen</th>
<th>Erstellt</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for o in open_orders %}
<tr>
<td class="id">{{ o.id[:8] }}…</td>
<td>{{ o.structure }}</td>
<td>{{ o.system|default('Jita') }}</td>
<td class="col-ind-struct">{{ o.industry_structure|default('Station') }}</td>
<td class="col-ind-rig">{{ o.industry_rig|default('None') }}</td>
<td class="col-react-struct">{{ o.reaction_structure|default('Athanor') }}</td>
<td class="col-react-rig">{{ o.reaction_rig|default('None') }}</td>
<td class="num">{{ o.quantity }}</td>
<td class="num">{{ o.me }}</td>
<td class="col-notes">{{ o.notes|default('') }}</td>
<td>{{ o.created_at|fmt_ts }}</td>
<td class="actions">
<a class="button" href="#m-{{ o.id }}">Details</a>
<button type="button" onclick="refreshOne('{{ o.id }}')">Aktualisieren</button>
<form method="post" action="/strukturen/done/{{ o.id }}"><button type="submit">Fertig</button></form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty">Keine offenen Aufträge.</div>
{% endif %}
</section>
<!-- Fertige Aufträge -->
<section class="card full">
<div class="section-title">Fertige Aufträge</div>
{% if done_orders %}
<div class="table-wrap scroll-5">
<table aria-label="Fertige Aufträge">
<thead>
<tr>
<th>ID</th>
<th>Struktur</th>
<th>System</th>
<th class="col-ind-struct">Ind.-Struktur</th>
<th class="col-ind-rig">Ind.-Rig</th>
<th class="col-react-struct">Reakt.-Struktur</th>
<th class="col-react-rig">Reakt.-Rig</th>
<th class="num">Menge</th>
<th class="num">ME&nbsp;%</th>
<th class="col-notes">Notizen</th>
<th>Erstellt</th>
<th>Fertig am</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for o in done_orders %}
<tr>
<td class="id">{{ o.id[:8] }}…</td>
<td>{{ o.structure }}</td>
<td>{{ o.system|default('Jita') }}</td>
<td class="col-ind-struct">{{ o.industry_structure|default('Station') }}</td>
<td class="col-ind-rig">{{ o.industry_rig|default('None') }}</td>
<td class="col-react-struct">{{ o.reaction_structure|default('Athanor') }}</td>
<td class="col-react-rig">{{ o.reaction_rig|default('None') }}</td>
<td class="num">{{ o.quantity }}</td>
<td class="num">{{ o.me }}</td>
<td class="col-notes">{{ o.notes|default('') }}</td>
<td>{{ o.created_at|fmt_ts }}</td>
<td>{{ o.done_at|fmt_ts }}</td>
<td class="actions">
<a class="button" href="#m-{{ o.id }}">Details</a>
<form method="post" action="/archiv/add/{{ o.id }}"><button type="submit">Archivieren</button></form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty">Noch keine fertigen Aufträge.</div>
{% endif %}
</section>
<!-- Kostenübersicht -->
<section class="card">
<div class="section-title" style="display:flex;justify-content:space-between;align-items:center">
<span>Kostenübersicht Offene Aufträge</span>
<button id="btn-refresh-all" type="button" onclick="refreshAll()">Aktualisieren</button>
</div>
<div id="costs-box" class="empty">Lade Kosten …</div>
<div id="costs-table" style="display:none">
<div class="table-wrap">
<table aria-label="Kostenübersicht">
<thead>
<tr>
<th>Struktur</th><th class="num">M</th><th class="num">ME</th><th>System</th><th>Industrie</th><th>Reaktion</th><th class="num">€/E</th><th class="num">Gesamt</th>
</tr>
</thead>
<tbody id="costs-body"></tbody>
<tfoot>
<tr><th colspan="7" class="num">Summe</th><th class="num"><strong id="costs-total">0 ISK</strong></th></tr>
</tfoot>
</table>
</div>
<div class="muted" id="costs-ts" style="margin-top:.4rem"></div>
</div>
</section>
<!-- Materialübersicht -->
<section class="card">
<div class="section-title" style="display:flex;justify-content:space-between;align-items:center">
<span>Materialübersicht Offene Aufträge</span>
<button id="btn-refresh-mats" type="button" onclick="refreshAll()">Aktualisieren</button>
</div>
<div id="mats-box" class="empty">Lade Materialien …</div>
<div id="mats-table" style="display:none">
<div class="table-wrap">
<table aria-label="Materialübersicht">
<thead>
<tr><th>Item</th><th class="num">Menge</th><th class="num">Kosten</th></tr>
</thead>
<tbody id="mats-body"></tbody>
<tfoot>
<tr><th colspan="2" class="num">Gesamtkosten</th><th class="num"><strong id="mats-total">0 ISK</strong></th></tr>
</tfoot>
</table>
</div>
<div class="muted" id="mats-ts" style="margin-top:.4rem"></div>
</div>
</section>
<!-- Modals -->
{% for o in open_orders + done_orders %}
<div id="m-{{ o.id }}" class="modal" aria-hidden="true">
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="h-{{ o.id }}">
<a class="close" href="#" aria-label="Schließen">×</a>
<h2 id="h-{{ o.id }}">Auftrag {{ o.structure }}</h2>
<div class="kv">
<div><strong>ID</strong></div><div class="id">{{ o.id }}</div>
<div><strong>Struktur</strong></div><div data-field="structure">{{ o.structure }}</div>
<div><strong>System</strong></div><div data-field="system">{{ o.system|default('Jita') }}</div>
<div><strong>Industrie-Struktur</strong></div><div data-field="industry_structure">{{ o.industry_structure|default('Station') }}</div>
<div><strong>Industrie-Rig</strong></div><div data-field="industry_rig">{{ o.industry_rig|default('None') }}</div>
<div><strong>Reaktions-Struktur</strong></div><div data-field="reaction_structure">{{ o.reaction_structure|default('Athanor') }}</div>
<div><strong>Reaktions-Rig</strong></div><div data-field="reaction_rig">{{ o.reaction_rig|default('None') }}</div>
<div><strong>Menge</strong></div><div data-field="quantity">{{ o.quantity }}</div>
<div><strong>ME&nbsp;%</strong></div><div data-field="me">{{ o.me }}</div>
<div><strong>Steuersatz&nbsp;%</strong></div><div data-field="facility_tax">{{ o.facility_tax|default(0) }}</div>
<div><strong>Notizen</strong></div><div class="note">{{ o.notes|default('') }}</div>
<div><strong>Erstellt</strong></div><div>{{ o.created_at|fmt_ts }}</div>
<div><strong>Blueprint&nbsp;ID</strong></div>
<div>
<input id="bp-{{ o.id }}" type="text" style="width:11rem"
value="{{ o.blueprint_type_id or '' }}" {{ '' if not o.blueprint_type_id else 'readonly' }} />
<button type="button" onclick="toggleEditBp('{{ o.id }}')">Bearbeiten</button>
<button type="button" onclick="saveBp('{{ o.id }}')">Speichern</button>
</div>
</div>
<hr class="sep" />
<h3 style="margin:.2rem 0 .4rem">Cookbook Kosten & Materialien</h3>
<div class="muted" style="margin-bottom:.4rem">Die Blueprint-ID ist vorausgefüllt und kann bei Bedarf überschrieben werden.</div>
<div style="display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;margin-bottom:.4rem">
<button type="button" onclick="fetchCookbook('{{ o.id }}')">Kosten abrufen</button>
<button type="button" onclick="fetchMaterials('{{ o.id }}')">Materialien abrufen</button>
<button type="button" onclick="refreshOne('{{ o.id }}')">Aktualisieren &amp; speichern</button>
<span id="cb-status-{{ o.id }}" class="muted"></span>
</div>
<div id="cb-total-{{ o.id }}" class="pill" style="display:none;margin-bottom:.5rem"></div>
<div id="cb-result-{{ o.id }}" class="result" style="display:none"></div>
<div id="cb-mats-{{ o.id }}" class="result" style="display:none"></div>
<div style="margin-top:.8rem">
{% if o.status == 'open' %}
<form method="post" action="/strukturen/done/{{ o.id }}" style="display:inline"><button type="submit">Als fertig markieren</button></form>
{% elif o.status == 'done' %}
<form method="post" action="/archiv/add/{{ o.id }}" style="display:inline"><button type="submit">Archivieren</button></form>
{% endif %}
<a class="button" href="#" style="margin-left:.4rem">Schließen</a>
</div>
</div>
</div>
{% endfor %}
</div>
<script>
function formatISK(x){const n=Number(x);if(!isFinite(n))return String(x);return n.toLocaleString('de-DE')+" ISK";}
function formatTS(s){
if(!s) return ""; const d=new Date(s);
if(isNaN(d.getTime())){ const t=String(s); return t.slice(0,16); }
const y=d.getFullYear(), m=String(d.getMonth()+1).padStart(2,'0'), da=String(d.getDate()).padStart(2,'0'),
hh=String(d.getHours()).padStart(2,'0'), mm=String(d.getMinutes()).padStart(2,'0');
return `${y}-${m}-${da} ${hh}:${mm}`;
}
function textOf(c,s){const el=c.querySelector(s);return el?el.textContent.trim():"";}
function toggleEditBp(id){const i=document.getElementById("bp-"+id);i.readOnly=!i.readOnly;i.focus();}
function saveBp(id){
const i=document.getElementById("bp-"+id), v=(i.value||"").trim();
if(!/^[0-9]+$/.test(v)){alert("Bitte gültige Blueprint-TypeID eingeben.");return;}
fetch("/api/order/"+encodeURIComponent(id)+"/bp",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({blueprint_type_id:v})}).then(()=>{i.readOnly=true;});
}
/* ===== Kosten ===== */
function pullTotal(d){
if(d&&d.total!=null) return d.total;
if(d&&d.totalCost!=null) return d.totalCost;
if(d&&d.message&&d.message.totalCost!=null) return d.message.totalCost;
return null;
}
function renderCost(root,data){
const m=(data&&data.message&&typeof data.message==='object')?data.message:data;
const rows=[
["Kosten / Einheit", m.buildCostPerUnit],
["Produzierte Menge", m.producedQuantity],
["Materialkosten", m.materialCost],
["Jobkosten", m.jobCost],
["Überschüssige Materialien", m.excessMaterialsValue],
["Zusatzkosten", m.additionalCost],
["Gesamtkosten", pullTotal(data)]
].filter(r=>r[1]!=null);
let html='<table style="width:100%;border-collapse:collapse"><tbody>';
rows.forEach(([k,v],i)=>{const strong=i===rows.length-1?' style="font-weight:700"':'';html+=`<tr><th style="text-align:left;padding:.25rem .35rem">${k}</th><td class="num" style="padding:.25rem .35rem"${strong}>${typeof v==='number'?formatISK(v):String(v)}</td></tr>`;});
html+='</tbody></table>';
const det=document.createElement('details'); const sum=document.createElement('summary'); sum.textContent='Rohdaten';
const pre=document.createElement('pre'); pre.style.whiteSpace='pre-wrap'; pre.textContent=JSON.stringify(data,null,2);
det.appendChild(sum); det.appendChild(pre);
root.innerHTML=html; root.appendChild(det);
}
/* ===== Materialien ===== (robustes Parsing) */
function looksLikeMatRow(o){
if(!o || typeof o!=='object') return false;
const hasId = ('type_id' in o) || ('typeId' in o);
const hasQty = ('quantity' in o) || ('qty' in o) || ('amount' in o) || ('count' in o);
return !!(hasId && hasQty);
}
function dictRowsToArray(obj){
const vals = Object.values(obj||{});
if(!vals.length) return null;
if(vals.every(looksLikeMatRow)) return vals;
return null;
}
function extractMaterials(data){
if(!data) return null;
if(data.materials){
if(Array.isArray(data.materials)) return data.materials;
const arr = dictRowsToArray(data.materials); if(arr) return arr;
}
if(data.message && typeof data.message==='object'){
const m = data.message;
if(m.materials){
if(Array.isArray(m.materials)) return m.materials;
const arr = dictRowsToArray(m.materials); if(arr) return arr;
}
}
let found=null;
(function walk(x){
if(found||!x) return;
if(Array.isArray(x)){
if(x.length && looksLikeMatRow(x[0])){ found=x; return; }
for(const y of x){ walk(y); if(found) return; }
}else if(typeof x==='object'){
const asArr = dictRowsToArray(x);
if(asArr){ found=asArr; return; }
for(const v of Object.values(x)){ walk(v); if(found) return; }
}
})(data);
return found;
}
function renderMaterials(root,data){
const mats = extractMaterials(data);
let html = "";
if(!mats || !mats.length){
html += "Keine Materialien gefunden.";
} else {
const rows=mats.map(m=>{
const typeId = m.type_id ?? m.typeId ?? "";
const name = m.name ?? m.item_name ?? m.typeName ?? ('Type '+typeId);
const qty = (m.quantity ?? m.qty ?? m.amount ?? m.count ?? "");
const cost = (m.cost ?? m.cost_per_unit ?? m.unit_cost);
const costHtml = (cost!=null)?('<strong>'+formatISK(cost)+'</strong>'):"";
return `<tr><td>${name} ${typeId?`<span class="muted">(${typeId})</span>`:""}</td><td class="num">${qty}</td><td class="num">${costHtml}</td></tr>`;
}).join("");
html += '<h4 style="margin:.2rem 0 .5rem">Materialien (Manufacturing)</h4>'+
`<table style="width:100%;border-collapse:collapse"><thead><tr><th>Item</th><th class="num">Menge</th><th class="num">Kosten</th></tr></thead><tbody>${rows}</tbody></table>`;
}
const det=document.createElement('details'); const sum=document.createElement('summary'); sum.textContent='Rohdaten';
const pre=document.createElement('pre'); pre.style.whiteSpace='pre-wrap'; pre.textContent=JSON.stringify(data,null,2);
det.appendChild(sum); det.appendChild(pre);
root.innerHTML=html; root.appendChild(det);
}
function saveCache(id,payload){ fetch("/api/order/"+encodeURIComponent(id)+"/cache",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload)}).catch(()=>{}); }
function loadCostsOverview(){
const box=document.getElementById("costs-box"), tbl=document.getElementById("costs-table"), body=document.getElementById("costs-body"), total=document.getElementById("costs-total"), ts=document.getElementById("costs-ts");
box.style.display="block"; box.textContent="Lade Kosten …"; tbl.style.display="none";
fetch("/api/cookbook/open_costs").then(r=>{if(!r.ok){return r.text().then(t=>{throw new Error('HTTP '+r.status+' - '+t.slice(0,200));});}return r.json();})
.then(d=>{
const items=d.items||[]; if(!items.length){box.textContent="Keine offenen Aufträge (oder keine Blueprint-IDs).";return;}
body.innerHTML=items.map(it=>{
const unit=(it.unit_cost!=null)?formatISK(it.unit_cost):"", tot=(it.total_cost!=null)?"<strong>"+formatISK(it.total_cost)+"</strong>":"";
const ind=(it.industry_structure||"")+((it.industry_rig&&it.industry_rig!=="None")?" / "+it.industry_rig:"");
const reac=(it.reaction_structure||"")+((it.reaction_rig&&it.reaction_rig!=="None")?" / "+it.reaction_rig:"");
const err=it.error?` <span class="err">(${it.error})</span>`:"";
return `<tr><td>${(it.structure||"")+err}</td><td class="num">${it.quantity||""}</td><td class="num">${it.me||""}</td><td>${it.system||""}</td><td>${ind}</td><td>${reac}</td><td class="num">${unit}</td><td class="num">${tot}</td></tr>`;
}).join("");
total.textContent=formatISK(d.total_cost||0);
ts.textContent=d.refreshed_at?("Stand: "+formatTS(d.refreshed_at)):"";
box.style.display="none"; tbl.style.display="block";
}).catch(err=>{box.innerHTML='<span class="err">Fehler: '+(err&&err.message?err.message:err)+'</span>'; tbl.style.display="none";});
}
function loadMatsOverview(){
const box=document.getElementById("mats-box"), tbl=document.getElementById("mats-table"), body=document.getElementById("mats-body"), total=document.getElementById("mats-total"), ts=document.getElementById("mats-ts");
box.style.display="block"; box.textContent="Lade Materialien …"; tbl.style.display="none";
fetch("/api/cookbook/open_materials").then(r=>{if(!r.ok){return r.text().then(t=>{throw new Error('HTTP '+r.status+' - '+t.slice(0,200));});}return r.json();})
.then(d=>{
const items=d.items||[]; if(!items.length){box.textContent="Keine Daten vorhanden.";return;}
body.innerHTML=items.map(it=>`<tr><td>${it.name||("Type "+it.type_id)} <span class="muted">(${it.type_id})</span></td><td class="num">${it.quantity!=null?it.quantity:""}</td><td class="num">${it.cost!=null?("<strong>"+formatISK(it.cost)+"</strong>"):""}</td></tr>`).join("");
total.textContent=formatISK(d.total_cost||0);
ts.textContent=d.refreshed_at?("Stand: "+formatTS(d.refreshed_at)):"";
box.style.display="none"; tbl.style.display="block";
}).catch(err=>{box.innerHTML='<span class="err">Fehler: '+(err&&err.message?err.message:err)+'</span>'; tbl.style.display="none";});
}
function refreshAll(){
const b1=document.getElementById("btn-refresh-all"), b2=document.getElementById("btn-refresh-mats");
if(b1) b1.disabled=true; if(b2) b2.disabled=true;
fetch("/api/orders/refresh_all",{method:"POST"}).then(r=>r.json()).then(()=>{
loadCostsOverview(); loadMatsOverview();
}).finally(()=>{ if(b1) b1.disabled=false; if(b2) b2.disabled=false; });
}
function refreshOne(id){
fetch("/api/order/"+encodeURIComponent(id)+"/refresh",{method:"POST"}).then(r=>r.json()).then(()=>{ loadCostsOverview(); loadMatsOverview(); });
}
document.addEventListener("DOMContentLoaded",function(){ loadCostsOverview(); loadMatsOverview(); });
</script>
</body>
</html>