Seiten-Editor: Inhalte pflegen ohne Code-Änderung
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Antwort auf den CMS-Wunsch aus der Community — statt eines Forums, das
neben aktivem Discord erfahrungsgemäß verwaist, ein schlanker Editor für
eigene Seiten: Regeln, Über uns, Mitmachen, FAQ.

- neuer Setup-Tab "Seiten" (Scope content): Titel, Adress-Kürzel wird aus
  dem Titel abgeleitet, Markdown-Editor mit Live-Vorschau daneben
- pro Seite wählbar: veröffentlicht oder Entwurf, im Community-Menü oder
  nur per Direktlink, Reihenfolge
- Anzeige unter /<kürzel>, gerendert mit dem vorhandenen Markdown-Parser
- Entwürfe sind öffentlich nicht abrufbar — sie brauchen den content-Scope
- reservierte Kürzel (devlogs, settings, api, impressum …) werden
  abgelehnt, damit nichts die festen Routen überschreibt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 15:08:21 +02:00
co-authored by Claude Fable 5
parent 3b80334433
commit e8158ce90b
8 changed files with 337 additions and 2 deletions
+15 -1
View File
@@ -15,6 +15,7 @@ import Home from './pages/Home.jsx';
import DevlogDetail from './pages/DevlogDetail.jsx';
import Impressum from './pages/Impressum.jsx';
import Datenschutz from './pages/Datenschutz.jsx';
import Seite from './pages/Seite.jsx';
import { IconChevronDown, IconMenu, IconX, IconLock } from './icons.jsx';
const COMMUNITY_LINKS = [
@@ -92,11 +93,16 @@ export default function App() {
// Member-Gate: OAuth war okay, aber User ist nicht auf dem Discord (?gate=1)
const [gated, setGated] = useState(() => new URLSearchParams(window.location.search).has('gate'));
const [invite, setInvite] = useState(null);
// Frei angelegte Seiten, die ins Menü sollen
const [pages, setPages] = useState([]);
useEffect(() => {
apiGet('/api/me')
.then((data) => setMe({ scopes: [], ...data, loading: false }))
.catch(() => setMe({ user: null, admin: false, scopes: [], loading: false }));
apiGet('/api/pages')
.then((d) => setPages(d.pages.filter((p) => p.in_menu)))
.catch(() => {});
}, []);
useEffect(() => {
@@ -126,6 +132,9 @@ export default function App() {
{COMMUNITY_LINKS.map(([to, label]) => (
<NavLink to={to} key={to}>{label}</NavLink>
))}
{pages.map((p) => (
<NavLink to={`/${p.slug}`} key={p.slug}>{p.title}</NavLink>
))}
</NavDropdown>
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">Setup</NavLink>}
</nav>
@@ -164,6 +173,9 @@ export default function App() {
{COMMUNITY_LINKS.map(([to, label]) => (
<NavLink to={to} key={to}>{label}</NavLink>
))}
{pages.map((p) => (
<NavLink to={`/${p.slug}`} key={p.slug}>{p.title}</NavLink>
))}
{me.user && <NavLink to="/profil">Profil</NavLink>}
{me.admin && <NavLink to="/commits">Commits</NavLink>}
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">Setup</NavLink>}
@@ -204,7 +216,9 @@ export default function App() {
<Route path="/datenschutz" element={<Datenschutz />} />
<Route path="/commits" element={<Commits me={me} />} />
<Route path="/settings" element={<Settings me={me} />} />
<Route path="*" element={<Navigate to="/devlogs" replace />} />
{/* Frei angelegte Seiten — muss hinter allen festen Routen stehen */}
<Route path="/:slug" element={<Seite />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</main>