Host-Routing: Bot-Produktseite und Community-Hub getrennt
Eine Anwendung, zwei Gesichter — der Server erkennt an der Domain, welche Seite gefragt ist, und schreibt das als data-site an den <body>. Das Frontend rendert daraufhin entweder den Community-Hub wie bisher oder die neue Produktseite. - Bot-Produktseite (BotApp): Landing mit Feature-Übersicht, Befehls- referenz, Dashboard unter /dashboard, Orange als Leitfarbe - Weiterleitungen: Community-Routen auf der Bot-Domain und umgekehrt werden dauerhaft (301) auf die richtige Adresse geschickt — geteilte Devlog-Permalinks laufen also nicht ins Leere. Rechtstexte bleiben auf beiden erreichbar. - Open-Graph-Tags je Domain, inklusive eigener Vorschau für frei angelegte Seiten (Entwürfe bekommen bewusst keine) - Login: neue Einstellung für die Cookie-Domain, damit die Anmeldung auf beiden Seiten gilt; nach dem Discord-Login landet man wieder auf der Seite, von der man gestartet ist (vorher immer auf der Hauptadresse) - Alles greift erst, wenn beide Adressen im Setup eingetragen sind — bis dahin verhält sich die Anwendung unverändert Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// Rahmen der Bot-Produktseite — eigene Navigation, orange Leitfarbe.
|
||||
// Das Dashboard ist dieselbe Setup-Seite wie bisher, nur unter /dashboard.
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Routes, Route, NavLink, Navigate, useLocation } from 'react-router-dom';
|
||||
import { apiGet } from './api.js';
|
||||
import BotLanding from './pages/BotLanding.jsx';
|
||||
import BotCommands from './pages/BotCommands.jsx';
|
||||
import Settings from './pages/Settings.jsx';
|
||||
import Impressum from './pages/Impressum.jsx';
|
||||
import Datenschutz from './pages/Datenschutz.jsx';
|
||||
import { IconMenu, IconX } from './icons.jsx';
|
||||
|
||||
export default function BotApp() {
|
||||
const [me, setMe] = useState({ user: null, admin: false, scopes: [], loading: true });
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [drawer, setDrawer] = useState(false);
|
||||
const [hub, setHub] = useState(null);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => { setDrawer(false); }, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
apiGet('/api/me')
|
||||
.then((data) => setMe({ scopes: [], ...data, loading: false }))
|
||||
.catch(() => setMe({ user: null, admin: false, scopes: [], loading: false }));
|
||||
apiGet('/api/legal').then((d) => setHub(d.publicUrl)).catch(() => {});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 24);
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
const canDashboard = me.admin || me.scopes?.length > 0;
|
||||
|
||||
return (
|
||||
<div className="app site-bot">
|
||||
<header className={`nav ${scrolled ? 'scrolled' : ''}`}>
|
||||
<NavLink to="/" className="nav-logo">
|
||||
D4RKBOT<span>.</span>
|
||||
</NavLink>
|
||||
|
||||
<nav className="nav-links">
|
||||
<NavLink to="/features">Funktionen</NavLink>
|
||||
<NavLink to="/commands">Befehle</NavLink>
|
||||
{canDashboard && <NavLink to="/dashboard">Dashboard</NavLink>}
|
||||
{hub && <a href={hub}>Community</a>}
|
||||
</nav>
|
||||
|
||||
<div className="auth">
|
||||
{me.loading ? null : me.user ? (
|
||||
<span className="user-chip">
|
||||
{me.user.avatar && <img className="avatar" src={me.user.avatar} alt="" />}
|
||||
<span className="username">{me.user.username}</span>
|
||||
<a className="logout" href="/auth/logout" title="Abmelden">
|
||||
<IconX size={13} />
|
||||
</a>
|
||||
</span>
|
||||
) : (
|
||||
<a className="btn-login" href="/auth/login">Anmelden</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button className="burger" onClick={() => setDrawer((d) => !d)} aria-label="Menü">
|
||||
{drawer ? <IconX size={20} /> : <IconMenu size={20} />}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{drawer && (
|
||||
<nav className="drawer" onClick={() => setDrawer(false)}>
|
||||
<NavLink to="/features">Funktionen</NavLink>
|
||||
<NavLink to="/commands">Befehle</NavLink>
|
||||
{canDashboard && <NavLink to="/dashboard">Dashboard</NavLink>}
|
||||
{hub && <a href={hub}>Community</a>}
|
||||
{me.user
|
||||
? <a href="/auth/logout">Abmelden</a>
|
||||
: <a href="/auth/login">Anmelden</a>}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<main className="main">
|
||||
<Routes>
|
||||
<Route path="/" element={<BotLanding />} />
|
||||
<Route path="/features" element={<BotLanding />} />
|
||||
<Route path="/commands" element={<BotCommands />} />
|
||||
<Route path="/dashboard" element={<Settings me={me} />} />
|
||||
<Route path="/impressum" element={<Impressum />} />
|
||||
<Route path="/datenschutz" element={<Datenschutz />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
<footer className="footer">
|
||||
<span className="footer-logo">D4RKBOT<span>.</span></span>
|
||||
<span className="footer-sep">//</span>
|
||||
<a href="https://git.d4rkst3r.de/D4rkst3r/d4rkbot" target="_blank" rel="noreferrer">
|
||||
Quellcode
|
||||
</a>
|
||||
<span className="footer-sep">//</span>
|
||||
<NavLink to="/impressum">Impressum</NavLink>
|
||||
<NavLink to="/datenschutz">Datenschutz</NavLink>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user