From 5de172f862078c605102e4a768ec68f2da20224d Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Sun, 9 Aug 2026 08:38:52 +0200 Subject: [PATCH] LS bekommt einen festen Platz unter Gaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das Modul stand nur unter "Meine Funktionen" — also solange, wie du es angeheftet laesst. Loest man die Nadel, ist es nur noch uebers Modul-Raster erreichbar. Fuer die groesste Seite im Panel zu wenig. Jetzt kann ein Modul `nav: true` setzen und bekommt damit einen Eintrag in der Seitenleiste, unter seiner eigenen Gruppe. Kein Sonderfall fuer LS: jedes Modul, das eine ganze Seite fuellt, kann sich so einsortieren. Dabei aufgefallen und mitgenommen: eine halbe Antwort von /api/modules/:id hat die ganze Config weiss gemacht. Der Ladepfad ist zwar sauber abgesichert — `fehler` und `!detail` fangen ab —, aber ein Objekt OHNE die Listen kommt durch beide Wachen und laesst dann `detail.werte.some(...)` werfen, was React mit dem gesamten Baum quittiert. Vier Listen mit `?? []` kosten nichts und machen aus dem Weissbild schlimmstenfalls eine leere Sektion. Zwei Werkzeug-Fallen nebenbei: `npx vite build` zieht mal das lokale Vite 6, mal ein gecachtes Vite 8 aus dem npx-Ordner — ab jetzt nur noch `npm --prefix frontend run build`. Und der laufende Preview-Server haelt dist/assets fest, was den Build mit EPERM abbricht; vorher stoppen. Geprueft: Seitenleiste zeigt Gaming mit Game-Server und LS, Klick oeffnet die Modulseite und markiert den Eintrag, kein Fehler in der Konsole. Co-Authored-By: Claude Opus 5 --- frontend/src/components/ModulePage.jsx | 37 +++++++++++++++----------- frontend/src/pages/Settings.jsx | 20 +++++++++++++- src/modules.js | 7 ++++- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/ModulePage.jsx b/frontend/src/components/ModulePage.jsx index 8ee0e55..511ff49 100644 --- a/frontend/src/components/ModulePage.jsx +++ b/frontend/src/components/ModulePage.jsx @@ -80,6 +80,13 @@ export default function ModulePage(props) { if (fehler) return

Diese Funktion konnte nicht geladen werden.

; if (!detail) return

Lade …

; + // Listen notfalls auffüllen. Eine halbe Antwort — abgeschnitten, veraltet, + // von einem Proxy verstümmelt — hat sonst die ganze Config weiß gemacht: + // `werte.some(...)` wirft, und React reisst den Baum mit. + const felder = detail.felder ?? []; + const texte = detail.texte ?? []; + const werte = detail.werte ?? []; + const missing = detail.missing ?? []; const Icon = moduleIcon(detail.id); @@ -97,7 +104,7 @@ export default function ModulePage(props) { async function werteSpeichern() { const geaendert = Object.fromEntries( Object.entries(werteEntwurf).filter(([wid, v]) => { - const jetzt = detail.werte.find((w) => w.id === wid); + const jetzt = werte.find((w) => w.id === wid); return jetzt && String(v) !== String(jetzt.value); }) ); @@ -125,7 +132,7 @@ export default function ModulePage(props) { } } - const werteOffen = detail.werte.some( + const werteOffen = werte.some( (w) => werteEntwurf[w.id] !== undefined && String(werteEntwurf[w.id]) !== String(w.value) ); @@ -159,22 +166,22 @@ export default function ModulePage(props) { - {detail.enabled && detail.missing.length > 0 && ( + {detail.enabled && missing.length > 0 && (

- Fehlt noch: {detail.missing.map((m) => m.label).join(', ')} + Fehlt noch: {missing.map((m) => m.label).join(', ')}

)} - {detail.enabled && detail.missing.length === 0 && ( + {detail.enabled && missing.length === 0 && (

Einsatzbereit

)} {/* Kanäle, Rollen und alles Weitere */} - {detail.felder.length > 0 && ( + {felder.length > 0 && (

Einstellungen

- {detail.felder.filter((f) => f.art !== 'farbe').map((f) => ( + {felder.filter((f) => f.art !== 'farbe').map((f) => (
{f.art !== 'schalter' && (
))} - {detail.felder.some((f) => f.art === 'farbe') && ( + {felder.some((f) => f.art === 'farbe') && (
- {detail.felder.filter((f) => f.art === 'farbe').map((f) => ( + {felder.filter((f) => f.art === 'farbe').map((f) => ( 0 && ( + {texte.length > 0 && (

Texte

- {detail.texte.map((t) => { + {texte.map((t) => { const entwurf = textEntwurf[t.id]; const wert = entwurf ?? t.text; const geaendert = entwurf != null && entwurf !== t.text; @@ -294,11 +301,11 @@ export default function ModulePage(props) { )} {/* Werte dieser Funktion */} - {detail.werte.length > 0 && ( + {werte.length > 0 && (

Werte

- {detail.werte.map((w) => { + {werte.map((w) => { const wert = werteEntwurf[w.id] ?? w.value; const geaendert = werteEntwurf[w.id] !== undefined && String(werteEntwurf[w.id]) !== String(w.value); return ( diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index 3d1e63d..8da47a7 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -4103,7 +4103,12 @@ export default function Settings({ me }) { {TAB_SECTIONS.map((section) => { const items = foundTabs.filter((t) => t.section === section.id); - if (items.length === 0) return null; + // Module mit `nav` bekommen hier einen festen Platz — + // sonst sind sie nur übers Raster erreichbar oder + // solange man sie angeheftet lässt. + const eigene = modules.filter((m) => m.nav && m.group === section.id + && (!query || m.name.toLowerCase().includes(query.toLowerCase()))); + if (items.length === 0 && eigene.length === 0) return null; return (
{section.label} @@ -4117,6 +4122,19 @@ export default function Settings({ me }) { {t.label} ))} + {eigene.map((m) => { + const Icon = moduleIcon(m.id); + return ( + + ); + })}
); })} diff --git a/src/modules.js b/src/modules.js index 5a4e22e..9fe27e0 100644 --- a/src/modules.js +++ b/src/modules.js @@ -247,7 +247,7 @@ export const MODULES = [ tool: { tab: 'server', label: 'Game-Server verwalten' }, }, { - id: 'ls_farm', group: 'gaming', name: 'LS — Höfe & Preise', + id: 'ls_farm', group: 'gaming', name: 'LS — Höfe & Preise', nav: true, desc: 'Landwirtschafts-Simulator: Besitzkarte mit Höfen, Fahrzeugen und Spielern, ' + 'dazu die Preiskurven übers Jahr. Braucht einen LS-Server mit Zugangs-Code unter „Game-Server".', tab: 'server', setting: 'ls_farm_enabled', @@ -481,6 +481,11 @@ export function moduleStates() { desc: mod.desc, group: mod.group, tab: mod.tab ?? null, + // Bekommt einen festen Platz in der Seitenleiste, unter der eigenen + // Gruppe. Für Module gedacht, die eine ganze Seite füllen — sonst + // erreicht man sie nur über das Raster oder als angeheftete Funktion, + // und dann steht ausgerechnet das grösste Modul nirgends. + nav: mod.nav === true, enabled: moduleEnabled(mod.id), missing: moduleMissing(mod), }));