This commit is contained in:
2025-11-12 17:18:16 +00:00
parent 9f4ff856e6
commit 67ade78747
3 changed files with 652 additions and 61 deletions

228
templates/new.html Normal file
View File

@@ -0,0 +1,228 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Satisfactory Production Calculator — New</title>
<style>
:root { --bg: #0f172a; --card: #111827; --text: #e5e7eb; --muted:#94a3b8; --accent:#22d3ee; --ok:#4ade80; }
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: var(--bg); color: var(--text); }
.container { margin: 0 auto; padding: 24px; }
h1 { font-size: 1.75rem; margin: 0 0 8px; }
p { color: var(--muted); margin-top: 0; }
.card { background: var(--card); border: 1px solid #1f2937; border-radius: 10px; padding: 16px; }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 800px) { .row { grid-template-columns: 1fr; } }
label { display:block; margin-bottom:8px; font-weight:600; }
select, input[type=number] { width: 100%; padding: 10px 12px; border-radius: 8px; border: 1px solid #334155; background: #0b1220; color: var(--text); }
button { padding: 10px 14px; border: 0; border-radius: 8px; background: var(--accent); color: #012b30; font-weight: 700; cursor: pointer; }
button:hover { filter: brightness(1.08); }
.mt { margin-top: 16px; }
.error { color: #fda4af; background: #451a1a; border: 1px solid #7f1d1d; padding: 10px; border-radius: 8px; }
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #1f2937; vertical-align: top; }
th { color: var(--muted); font-weight: 600; }
code { color: var(--accent); }
.mono { font-variant-numeric: tabular-nums; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.pill { display:inline-block; padding:2px 8px; border-radius: 999px; background:#0b1220; border:1px solid #334155; color: var(--muted); font-size: 12px; }
</style>
<link rel="preconnect" href="/">
<meta name="robots" content="noindex">
<meta name="color-scheme" content="dark">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Satisfactory Production Calculator (new build-chain view)">
<link rel="icon" href="data:,">
<base href="/new">
<style>
details summary { cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>Satisfactory Production Calculator — New</h1>
<p>Compute production using the new build-chain logic. This page mirrors the index UI.</p>
<form class="card" method="get" id="targets-form">
<div>
<label>Products</label>
<table style="width:100%; border-collapse: collapse;">
<thead>
<tr>
<th style="width:60%">Item</th>
<th style="width:30%" class="mono">Rate (items/min)</th>
<th style="width:10%"></th>
</tr>
</thead>
<tbody id="targets-rows">
{% for row in targets_ui %}
<tr class="target-row">
<td>
<select name="item_{{ loop.index }}" required>
{% for it in items %}
<option value="{{ it }}" {% if row.item == it %}selected{% endif %}>{{ it }}</option>
{% endfor %}
</select>
</td>
<td>
<input name="rate_{{ loop.index }}" type="number" step="0.01" min="0" value="{{ row.rate }}" required>
</td>
<td>
<button type="button" class="pill" onclick="removeRow(this)" title="Remove"></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="mt">
<button type="button" onclick="addRow()">Add product</button>
</div>
</div>
<div class="mt">
<button type="submit">Calculate</button>
<a href="{{ reset_query }}" class="pill" style="margin-left:8px; display:inline-block; text-decoration:none;">Reset</a>
</div>
{% if error %}
<div class="mt error">{{ error }}</div>
{% endif %}
</form>
{% if chain and chain|length > 0 %}
<div class="mt card">
<h2 style="margin-top:0">Results</h2>
<form method="get" style="background: transparent; border:0; padding:0;">
{# Preserve all target rows in the recalculation #}
{% for row in targets_ui %}
<input type="hidden" name="item_{{ loop.index }}" value="{{ row.item }}">
<input type="hidden" name="rate_{{ loop.index }}" value="{{ row.rate }}">
{% endfor %}
{# Separate table: Alternate recipe choices organized by item #}
{% if alternates_by_item and alternates_by_item|length > 0 %}
<h3>Alternate recipes by item</h3>
<table class="mt">
<thead>
<tr>
<th style="width:40%">Item</th>
<th style="width:60%">Preferred recipe</th>
</tr>
</thead>
<tbody>
{% for row in alternates_by_item %}
<tr>
<td>{{ row.item }}</td>
<td>
<select name="recipe_for_{{ row.slug }}">
{% for opt in row.options %}
<option value="{{ opt.name }}" {% if opt.name == row.selected %}selected{% endif %}>{{ opt.name }} ({{ opt.building }})</option>
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<table>
<thead>
<tr>
<th>Recipe</th>
<th>Building</th>
<th class="mono">Production level</th>
<th>Ingress</th>
<th>Outputs</th>
<th>Egress</th>
</tr>
</thead>
<tbody>
{% for p in chain %}
<tr>
<td>{{ p.recipe }}</td>
<td>{{ p.building }}</td>
<td class="mono">{{ '%.2f'|format(p.production_level) }}</td>
<td>
{% if p.ingress and p.ingress|length > 0 %}
<div>
{% for ing in p.ingress %}
<div>{{ ing.item }} ← {{ ing.via }} — <span class="mono">{{ '%.2f'|format(ing.rate) }}</span></div>
{% endfor %}
</div>
{% else %}
<span class="pill">None</span>
{% endif %}
</td>
<td>
{% if p.outputs and p.outputs|length > 0 %}
<div>
{% for out in p.outputs %}
<div>{{ out.item }} — <span class="mono">{{ '%.2f'|format(out.rate) }}</span></div>
{% endfor %}
</div>
{% else %}
<span class="pill">None</span>
{% endif %}
</td>
<td>
{% if p.egress and p.egress|length > 0 %}
<div>
{% for eg in p.egress %}
<div>{{ eg.item }} → {{ eg.to }} — <span class="mono">{{ '%.2f'|format(eg.rate) }}</span></div>
{% endfor %}
</div>
{% else %}
<span class="pill">None</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="mt">
<button type="submit">Recalculate</button>
<a href="{{ reset_query }}" class="pill" style="margin-left:8px; display:inline-block; text-decoration:none;">Reset overrides</a>
</div>
</form>
</div>
{% endif %}
</div>
<script>
// Dynamic add/remove for multiple product targets
const itemsList = {{ items | tojson }};
function addRow() {
const tbody = document.getElementById('targets-rows');
const idx = tbody.querySelectorAll('tr').length + 1;
const tr = document.createElement('tr');
tr.className = 'target-row';
const options = itemsList.map(it => `<option value="${it}">${it}</option>`).join('');
tr.innerHTML = `
<td>
<select name="item_${idx}" required>${options}</select>
</td>
<td>
<input name="rate_${idx}" type="number" step="0.01" min="0" value="60.0" required>
</td>
<td>
<button type="button" class="pill" onclick="removeRow(this)" title="Remove">✕</button>
</td>`;
tbody.appendChild(tr);
}
function removeRow(btn) {
const tr = btn.closest('tr');
const tbody = tr && tr.parentElement;
if (!tbody) return;
tbody.removeChild(tr);
// Renumber names to keep indices compact
Array.from(tbody.querySelectorAll('tr')).forEach((row, i) => {
const idx = i + 1;
const sel = row.querySelector('select[name^="item_"]');
const rate = row.querySelector('input[name^="rate_"]');
if (sel) sel.name = `item_${idx}`;
if (rate) rate.name = `rate_${idx}`;
});
}
</script>
</body>
</html>