Umsetzung des Entwurfs aus design_handoff_redesign/ -- beide Auftritte (Produktseite und Community-Hub) plus Dashboard. Farbe #0a0a0a + Neon-Gelb -> #131316 + Orange-Verlauf aus dem Logo Schrift Bebas/Barlow/ShareTech -> Chakra Petch + IBM Plex Mono Form border-radius -> clip-path, oben links + unten rechts Rauschen, Scanlines, Hintergrundschrift, Kopf-Raster und die Partikel sind raus; der Schein von oben rechts ersetzt sie. Die Abschraegung ist ein System und keine sechzig Einzelregeln: eine clip-path-Regel mit --bv, jede Komponente setzt nur die Tiefe. Der Farbdurchstich ging durch alle ~55 Abschnitte der style.css, nicht nur durch die fuenf gezeichneten Seiten -- sonst waeren Galerie, Level, Composer und Emoji-Auswahl im alten Gelb stehen geblieben. Von --neon ist nichts uebrig. Die Schriften kommen weiter self-hosted ueber @fontsource. Der Entwurf schlaegt einen Google-Fonts-Link vor; das waere eine Fremdanfrage mehr, wo der Aufbau bisher ohne auskam. ZWEI FEHLER unterwegs gefunden, beide aelter als dieser Umbau: 1. .btn stand in der Datei NACH .btn-save und .btn-ghost. Bei gleicher Spezifitaet gewinnt die spaetere Regel, also hat ihr `background: none` jeden Verlauf ueberschrieben -- der Speichern-Knopf sah aus wie ein Ghost-Knopf, schon in der gelben Fassung. Die Grundform steht jetzt vor allen Abwandlungen. 2. .nav-logo ist ein Flex-Container, und `gap` legte sich zwischen die Textknoten "D4RKST" und <span>3R</span>. In der Leiste stand "D4RKST 3R". Der Abstand haengt jetzt am Bild statt am Container. DREI ABWEICHUNGEN vom Entwurf, bewusst: - Die Statuszeile im Hero zeigt nur "ONLINE", nicht "38 MS · UPTIME 99,2 %". Antwortzeit und Erreichbarkeit des Bots liegen ausschliesslich hinter /api/settings und damit hinter der Anmeldung; oeffentlich gibt es sie nirgends. Dafuer braeuchte es einen neuen Endpunkt -- erfunden wird hier nichts. - Keine Aura am Hauptknopf: clip-path schneidet den Schatten des eigenen Elements mit weg. Im Referenz-HTML stehen box-shadow und clip-path am selben Element, dort ist sie also ebenso wenig zu sehen. Tote Deklaration weggelassen statt mitgeschleppt. - Die Config-Bereiche bleiben scharfkantig statt abgeschraegt. In ihnen klappen Auswahllisten aus dem Panel heraus, die clip-path abschneiden wuerde. Deckt sich mit der Vorgabe, dass die uebrigen Tabs nur die Token uebernehmen; die Dashboard-Panels selbst sind abgeschraegt. Dazu: .content von 860 auf 1160 px, weil die dreispaltigen Raster es brauchen -- die Devlog-Timeline behaelt 880 px Lesebreite, sonst waeren die Zeilen zu lang. Der Punkt an den Timeline-Karten ist entfallen: er sass ausserhalb der Karte und waere von der Abschraegung weggeschnitten worden. Welches Wort in einer Ueberschrift den Verlauf bekommt, steht jetzt im Woerterbuch -- der Teil in eckigen Klammern ("Funk[tionen]", aber "Fea[tures]"). So entscheidet jede Sprache selbst, wo sich das Wort teilen laesst. Mitgezogen: brand.js, das geteilte Design-System fuer die eingebundenen Dienste. Neue Tokens und Abschraegungen, aber --neon/--neon2 bleiben als Zweitname stehen -- fremde Apps benutzen sie in ihrem eigenen CSS. Marken-Dateien nach frontend/public/brand/, verkleinert auf die Groessen, in denen sie wirklich dargestellt werden: 2,4 MB -> 97 KB. Geprueft: Build laeuft. Im Browser durchgesehen -- Landing, Funktionen, Befehle, Hub-Start, Devlogs, Server, Roadmap, Dashboard. Ungeprueft: das Dashboard mit echter Anmeldung. Eine Sitzung war hier nicht moeglich, gesehen habe ich es ueber gestubbte API-Antworten. Ebenso ungeprueft, wie sich die Schriftumstellung auf schmalen Schirmen macht. NICHT angefasst: die Embed-Farben in runtime-settings.js stehen weiter auf dem alten Gelb -- die wirken in Discord, nicht auf der Webseite. Und ist brand_color in der Datenbank gesetzt, behalten die eingebundenen Dienste ihre bisherige Farbe, unabhaengig von den neuen Vorgabewerten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
122 lines
5.7 KiB
React
122 lines
5.7 KiB
React
// Rahmen der Bot-Produktseite — eigene Navigation, gleiche Marke wie der Hub.
|
|
// Das Dashboard ist dieselbe Config-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 BotFeatures from './pages/BotFeatures.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';
|
|
import { useT, LangSwitch } from './i18n.jsx';
|
|
|
|
export default function BotApp() {
|
|
const [me, setMe] = useState({ user: null, admin: false, scopes: [], loading: true });
|
|
const [drawer, setDrawer] = useState(false);
|
|
const [hub, setHub] = useState(null);
|
|
// Seiten wie die Nutzungsbedingungen liegen auf dem Hub — verlinkt
|
|
// wird deshalb absolut, und nur wenn es einen Hub gibt.
|
|
const [pages, setPages] = useState([]);
|
|
const location = useLocation();
|
|
const { t } = useT();
|
|
|
|
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.split ? d.hubUrl : null)).catch(() => {});
|
|
apiGet('/api/pages').then((d) => setPages(d.pages)).catch(() => {});
|
|
}, []);
|
|
|
|
const canDashboard = me.admin || me.scopes?.length > 0;
|
|
|
|
return (
|
|
<div className="app">
|
|
{/* Die Leiste ist immer kompakt — kein Zustandswechsel beim Scrollen */}
|
|
<header className="nav">
|
|
<NavLink to="/" className="nav-logo">
|
|
<img className="nav-mark" src="/brand/mark.png" alt="" />
|
|
D4RKBOT<span>_</span>
|
|
</NavLink>
|
|
|
|
<nav className="nav-links">
|
|
<NavLink to="/features">{t('nav.features')}</NavLink>
|
|
<NavLink to="/commands">{t('nav.commands')}</NavLink>
|
|
{canDashboard && <NavLink to="/dashboard">{t('nav.dashboard')}</NavLink>}
|
|
{hub && <a href={hub}>{t('nav.community')}</a>}
|
|
</nav>
|
|
|
|
<div className="auth">
|
|
<LangSwitch />
|
|
{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={t('nav.signOut')}>
|
|
<IconX size={13} />
|
|
</a>
|
|
</span>
|
|
) : (
|
|
<a className="btn-login" href="/auth/login">{t('nav.signIn')}</a>
|
|
)}
|
|
</div>
|
|
|
|
<button className="burger" onClick={() => setDrawer((d) => !d)} aria-label={t('nav.menu')}>
|
|
{drawer ? <IconX size={20} /> : <IconMenu size={20} />}
|
|
</button>
|
|
</header>
|
|
|
|
{drawer && (
|
|
<nav className="drawer" onClick={() => setDrawer(false)}>
|
|
<NavLink to="/features">{t('nav.features')}</NavLink>
|
|
<NavLink to="/commands">{t('nav.commands')}</NavLink>
|
|
{canDashboard && <NavLink to="/dashboard">{t('nav.dashboard')}</NavLink>}
|
|
{hub && <a href={hub}>{t('nav.community')}</a>}
|
|
{me.user
|
|
? <a href="/auth/logout">{t('nav.signOut')}</a>
|
|
: <a href="/auth/login">{t('nav.signIn')}</a>}
|
|
{/* Auf schmalen Schirmen ist .auth ausgeblendet — der
|
|
Sprachwechsel muss trotzdem erreichbar bleiben. */}
|
|
<div className="drawer-lang" onClick={(e) => e.stopPropagation()}>
|
|
<LangSwitch />
|
|
</div>
|
|
</nav>
|
|
)}
|
|
|
|
<main className="main">
|
|
<Routes>
|
|
<Route path="/" element={<BotLanding />} />
|
|
<Route path="/features" element={<BotFeatures />} />
|
|
<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">
|
|
<img className="footer-mark" src="/brand/mark.png" alt="" />
|
|
D4RKBOT<span>_</span>
|
|
</span>
|
|
<a href="https://git.d4rkst3r.de/D4rkst3r/d4rkbot" target="_blank" rel="noreferrer">
|
|
{t('footer.source')}
|
|
</a>
|
|
{hub && <a href={`${hub}/status`}>{t('footer.status')}</a>}
|
|
<span className="footer-right">
|
|
<NavLink to="/impressum">{t('footer.imprint')}</NavLink>
|
|
<NavLink to="/datenschutz">{t('footer.privacy')}</NavLink>
|
|
{hub && pages.map((p) => (
|
|
<a href={`${hub}/${p.slug}`} key={p.slug}>{p.title}</a>
|
|
))}
|
|
</span>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|