chore: initial import
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>boehmitools · Einstellungen</title>
|
||||
<link rel="stylesheet" href="/shared/boehmi.css">
|
||||
<style>
|
||||
.field { margin-bottom: 12px; }
|
||||
.field .help { font-size: 11.5px; color: var(--bt-muted); margin-top: 4px; }
|
||||
.sticky-actions {
|
||||
position: sticky; bottom: 0; margin-top: 18px; padding: 12px 0;
|
||||
background: linear-gradient(transparent, var(--bt-bg) 40%);
|
||||
display: flex; gap: 8px; align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-bt-title="Einstellungen" data-bt-icon="⚙">
|
||||
|
||||
<main class="bt-main narrow">
|
||||
<div class="bt-pagehead">
|
||||
<h1>Einstellungen</h1>
|
||||
<p>Diese Zugänge gelten für alle Tools der Suite. Sie liegen in
|
||||
<code>data/settings.json</code> und werden den Tools als Umgebungsvariablen
|
||||
übergeben — genau so, wie sie es als Einzeltool erwarten.</p>
|
||||
</div>
|
||||
|
||||
<div class="bt-notice info">
|
||||
Tokens und Schlüssel bleiben im lokalen Backend. Bereits gespeicherte
|
||||
Geheimnisse werden hier nur maskiert angezeigt; ein leeres Feld lässt den
|
||||
bestehenden Wert unverändert.
|
||||
</div>
|
||||
|
||||
<div id="groups"></div>
|
||||
|
||||
<div class="sticky-actions">
|
||||
<button class="primary" id="save">Speichern</button>
|
||||
<span id="state" class="bt-muted"></span>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="/shared/boehmi.js"></script>
|
||||
<script>
|
||||
const groupsEl = document.getElementById("groups");
|
||||
const stateEl = document.getElementById("state");
|
||||
let schema = [];
|
||||
|
||||
function fieldHtml(f, value) {
|
||||
const id = "f_" + f.key;
|
||||
let control;
|
||||
if (f.kind === "bool") {
|
||||
control = `<label class="bt-check"><input type="checkbox" id="${id}"
|
||||
${String(value).toLowerCase() === "true" ? "checked" : ""}> aktiv</label>`;
|
||||
} else if (f.kind === "select") {
|
||||
control = `<select id="${id}">${f.options.map((o) =>
|
||||
`<option ${o === value ? "selected" : ""}>${BT.escape(o)}</option>`).join("")}</select>`;
|
||||
} else if (f.kind === "secret") {
|
||||
control = `<input type="password" id="${id}" value="${BT.escape(value)}"
|
||||
placeholder="${value ? "unverändert" : "nicht gesetzt"}" autocomplete="new-password">`;
|
||||
} else {
|
||||
control = `<input type="${f.kind === "number" ? "number" : "text"}" id="${id}"
|
||||
value="${BT.escape(value)}" placeholder="${BT.escape(f.placeholder || f.default || "")}">`;
|
||||
}
|
||||
return `<div class="field">
|
||||
<label for="${id}">${BT.escape(f.label)}</label>
|
||||
${control}
|
||||
${f.help ? `<div class="help">${BT.escape(f.help)}</div>` : ""}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const data = await fetch("/api/settings").then((r) => r.json());
|
||||
schema = data.schema;
|
||||
groupsEl.innerHTML = schema.map((g) => `
|
||||
<section class="bt-card">
|
||||
<h2>${BT.escape(g.group)}</h2>
|
||||
<p class="hint">${g.group === "Tandoor"
|
||||
? "Wird vom AI-Webimport, vom Rezeptimport und vom Vereinheitlichen genutzt."
|
||||
: g.group === "OpenAI" ? "Nur für den AI-Webimport nötig."
|
||||
: "Schutz beim Abruf externer Rezeptseiten."}</p>
|
||||
${g.fields.map((f) => fieldHtml(f, data.values[f.key] ?? "")).join("")}
|
||||
</section>`).join("");
|
||||
}
|
||||
|
||||
document.getElementById("save").addEventListener("click", async () => {
|
||||
const payload = {};
|
||||
schema.forEach((g) => g.fields.forEach((f) => {
|
||||
const el = document.getElementById("f_" + f.key);
|
||||
if (!el) return;
|
||||
payload[f.key] = f.kind === "bool" ? String(el.checked) : el.value;
|
||||
}));
|
||||
try {
|
||||
stateEl.textContent = "Speichern …";
|
||||
await BT.api("/api/settings", { method: "POST", body: JSON.stringify(payload) });
|
||||
stateEl.textContent = "";
|
||||
BT.toast("Einstellungen gespeichert.", "ok");
|
||||
load();
|
||||
} catch (e) {
|
||||
stateEl.textContent = "";
|
||||
BT.toast(e.message, "err");
|
||||
}
|
||||
});
|
||||
|
||||
load();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user