From de4e812896be5af659a88651407f4ddf85a1b0ce Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Fri, 31 Jul 2026 22:00:36 +0200 Subject: [PATCH] =?UTF-8?q?Werte=20f=C3=BCr=20mehrere=20Funktionen,=20und?= =?UTF-8?q?=20Anlegen=20frischt=20die=20Auswahl=20auf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Nachzügler zu den Modul-Seiten. Die Thread-Archivdauer gilt für Tickets und Modmail, hing aber nur an Tickets — auf der Modmail-Seite fehlte sie also. `module` darf jetzt eine Liste sein; nach außen kommt immer eine, damit niemand zwischen einem und mehreren unterscheiden muss. Und beim Anlegen aus dem Panel wurde nur die Einstellung gesetzt, nicht die Kanalliste nachgeladen. Der frische Kanal stand damit zwar in der Einstellung, fehlte aber in der Auswahl — das Feld sah leer aus, obwohl es gesetzt war. Aufgefallen im Browser-Test: „Einsatzbereit" stand da, die Auswahl daneben war leer. Jetzt holt reloadSettings Kanäle, Rollen und Einstellungen frisch; das gilt für den Einzelknopf, den Assistenten und die Modul-Seiten gleichermaßen. Nachgeprüft: Kanal über die Status-Liste anlegen, dann ohne Neuladen nach Feeds wechseln — dort steht #dev-log. Co-Authored-By: Claude Fable 5 --- frontend/src/components/ModulePage.jsx | 10 ++++++---- frontend/src/pages/Settings.jsx | 21 +++++++++++++++------ src/tuning.js | 11 ++++++++--- src/web/api.js | 2 +- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/ModulePage.jsx b/frontend/src/components/ModulePage.jsx index de9bc9a..f164e92 100644 --- a/frontend/src/components/ModulePage.jsx +++ b/frontend/src/components/ModulePage.jsx @@ -59,12 +59,12 @@ function Feld({ feld, value, onChange, PickSelect, channelItems, roleItems }) { * form: object, setForm: (f: object) => void, * PickSelect: Function, channelItems: Array, roleItems: Array, * welcomePreviewUrl: string, flash: (msg: string) => void, - * goTab: (tab: string) => void, + * goTab: (tab: string) => void, reloadSettings: () => Promise, * }} props */ export default function ModulePage(props) { const { id, onBack, onToggle, form, setForm, PickSelect, channelItems, roleItems, - welcomePreviewUrl, flash, goTab } = props; + welcomePreviewUrl, flash, goTab, reloadSettings } = props; const [detail, setDetail] = useState(null); const [fehler, setFehler] = useState(false); @@ -104,7 +104,7 @@ export default function ModulePage(props) { if (Object.keys(geaendert).length === 0) return; try { const res = await apiPut('/api/tuning', geaendert); - setDetail((d) => ({ ...d, werte: res.values.filter((w) => w.module === d.id) })); + setDetail((d) => ({ ...d, werte: res.values.filter((w) => w.modules.includes(d.id)) })); setWerteEntwurf({}); flash('✓ Werte gespeichert'); } catch (error) { @@ -115,7 +115,9 @@ export default function ModulePage(props) { async function anlegen(setting) { try { const res = await apiPost('/api/setup/create', { setting }); - setForm(res.settings); + // Auffrischen statt nur setzen: der neue Kanal muss auch in der + // Auswahlliste stehen, sonst wirkt das Feld leer. + await reloadSettings(); laden(); flash(res.wiederverwendet ? `✓ „${res.name}" verknüpft` : `✓ „${res.name}" angelegt`); } catch (error) { diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index 104c59e..d6265cc 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -542,8 +542,7 @@ export default function Settings({ me }) { try { const res = await apiPost('/api/setup/run', { kategorie: plan.kategorie }); setModules(res.modules); - setForm(res.settings); - setData((d) => (d ? { ...d, settings: res.settings } : d)); + await reloadSettings(); setSetupResult(res.ergebnisse); setPlan(null); const neu = res.ergebnisse.filter((r) => r.ok && !r.wiederverwendet).length; @@ -555,16 +554,25 @@ export default function Settings({ me }) { } } + /** + * Kanäle, Rollen und Einstellungen neu holen. Nötig nach dem Anlegen: der + * frische Kanal steht sonst zwar in der Einstellung, fehlt aber in der + * Auswahlliste — das Feld sähe leer aus, obwohl es gesetzt ist. + */ + async function reloadSettings() { + const frisch = await apiGet('/api/settings').catch(() => null); + if (!frisch) return; + setData(frisch); + setForm(frisch.settings); + } + /** Fehlenden Kanal / fehlende Rolle vom Bot anlegen lassen */ async function createMissing(entry) { setCreating(entry.setting); try { const res = await apiPost('/api/setup/create', { setting: entry.setting }); setModules(res.modules); - // Die neue Auswahl steht sofort im Formular — und zählt nicht als - // ungespeicherte Änderung, weil der Vergleichsstand mitwandert. - setForm(res.settings); - setData((d) => (d ? { ...d, settings: res.settings } : d)); + await reloadSettings(); flash(res.wiederverwendet ? `✓ Vorhandenen ${res.art === 'rolle' ? 'Rolle' : 'Kanal'} „${res.name}" verknüpft` : `✓ ${res.art === 'rolle' ? 'Rolle' : 'Kanal'} „${res.name}" angelegt`); @@ -2378,6 +2386,7 @@ export default function Settings({ me }) { welcomePreviewUrl={welcomePreviewUrl} flash={flash} goTab={setTab} + reloadSettings={reloadSettings} /> ) : (
diff --git a/src/tuning.js b/src/tuning.js index 8588d43..3089c7c 100644 --- a/src/tuning.js +++ b/src/tuning.js @@ -2,7 +2,9 @@ // Wartezeiten, Prüf-Intervalle, Obergrenzen. // // `module` verbindet einen Wert mit seiner Funktion — die Modul-Seite zeigt ihn -// dann dort, wo er hingehört, statt nur in der flachen Werte-Liste. +// dann dort, wo er hingehört, statt nur in der flachen Werte-Liste. Ein Wert +// darf zu mehreren gehören (die Thread-Archivdauer gilt für Tickets UND +// Modmail); dann steht dort eine Liste. // // Wie bei den Modulen und Vorlagen lebt der Standard im Code und die // Datenbank enthält nur Abweichungen. `min`/`max` sind keine Kosmetik: sie @@ -107,7 +109,7 @@ export const TUNING = [ unit: 'Commits', default: 10, min: 1, max: 20, }, { - id: 'thread_archive_days', module: 'tickets', group: 'grenzen', label: 'Threads archivieren nach', + id: 'thread_archive_days', module: ['tickets', 'modmail'], group: 'grenzen', label: 'Threads archivieren nach', hint: 'Für Ticket- und Modmail-Threads. Discord erlaubt 1, 3 oder 7 Tage.', unit: 'Tagen', default: 7, min: 1, max: 7, choices: [1, 3, 7], }, @@ -171,7 +173,10 @@ export function tuningStates() { const custom = getSetting(`tune_${t.id}`); return { id: t.id, label: t.label, hint: t.hint ?? null, - group: t.group, module: t.module ?? null, + group: t.group, + // Immer als Liste nach außen — der Aufrufer muss dann nicht + // zwischen einem Wert und mehreren unterscheiden + modules: t.module ? [t.module].flat() : [], unit: t.unit, min: t.min, max: t.max, choices: t.choices ?? null, default: t.default, diff --git a/src/web/api.js b/src/web/api.js index ee8cdbc..0bebec6 100644 --- a/src/web/api.js +++ b/src/web/api.js @@ -202,7 +202,7 @@ export function registerApiRoutes(app, client) { return { ...detail, texte: templateStates().filter((t) => t.module === detail.id), - werte: tuningStates().filter((w) => w.module === detail.id), + werte: tuningStates().filter((w) => w.modules.includes(detail.id)), }; });