Bestenliste, Devlog-Filter, Galerie und leere Bereiche
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Vier Stellen, die beim Durchsehen der Seiten aufgefallen sind.

Die Bestenliste zeigte nur Namen — und für Platz 1 bis 3 denselben Pokal
ohne Nummer, wodurch die Reihenfolge nur an der Position erkennbar war.
Jetzt: Rangnummer, Avatar und die ersten drei Plätze farblich abgesetzt.
Die Avatare kommen aus dem Member-Cache, der durch den GuildMembers-Intent
ohnehin gefüllt ist — das kostet keinen einzigen Discord-Aufruf. Wer nicht
im Cache liegt, bekommt seinen Anfangsbuchstaben statt eines leeren Kreises.

Die Projekt-Chips im Devlog-Archiv waren bloß Beschriftung. Jetzt filtern
Chips mit Anzahl nach Projekt (ECOGAME / D4RKBOT) — man will meist nur
eins von beiden lesen. Serverseitig über LIKE auf die erste Zeile, wo das
Projekt ohnehin steht; das spart eine Spalte und eine Migration. Eine
aktive Suche schlägt den Filter, sonst müsste die Volltextsuche beides
verbinden, ohne dass jemand danach fragt.

Galerie: Vorschaubilder von 240 auf 340 Pixel, und Name plus Datum stehen
dauerhaft am Bild statt nur beim Überfahren — am Handy gab es kein Hover
und damit nie eine Zuordnung.

Events, Changelog, Galerie und Level endeten bei wenig Inhalt mit einem
einzelnen Satz in einer sonst leeren Seite; das sah nach Fehler aus statt
nach „noch nichts da". Jetzt steht dort ein Block mit Symbol, Erklärung
was hier erscheinen wird, und Verweisen auf belebte Bereiche.

Dazu ein Sprung-nach-oben-Knopf im Devlog-Archiv und im Changelog, die
beide sehr lang werden.

Beim Testen gefunden: ESCAPE '\' in einem JS-String kommt als leeres
Zeichen in SQLite an ("ESCAPE expression must be a single character") —
der Backslash müsste doppelt entwertet werden. Jetzt ist das Escape-Zeichen
ein Ausrufezeichen, das die Falle gar nicht erst aufmacht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 17:44:57 +02:00
co-authored by Claude Fable 5
parent c6977f9c4a
commit 2e58268f8d
11 changed files with 314 additions and 32 deletions
+33
View File
@@ -0,0 +1,33 @@
// Leerer Bereich mit Substanz: Symbol, Überschrift, ein Satz was hier erscheint
// und wohin es stattdessen geht.
//
// Vorher stand auf Events, Changelog, Galerie und Level je ein einzelner Satz
// in einer sonst leeren Seite — das sah nach Fehler aus statt nach „noch nichts da".
import { Link } from 'react-router-dom';
/**
* @param {{
* Icon: (props: object) => JSX.Element,
* title: string,
* text: string,
* actions?: Array<{ to?: string, href?: string, label: string }>,
* }} props
*/
export default function EmptyState({ Icon, title, text, actions = [] }) {
return (
<div className="empty-state">
<span className="empty-icon"><Icon size={28} /></span>
<h2 className="empty-title">{title}</h2>
<p className="empty-text">{text}</p>
{actions.length > 0 && (
<div className="empty-actions">
{actions.filter((a) => a.to || a.href).map((a) => (
a.to
? <Link className="btn" to={a.to} key={a.label}>{a.label}</Link>
: <a className="btn" href={a.href} key={a.label} target="_blank" rel="noreferrer">{a.label}</a>
))}
</div>
)}
</div>
);
}
+27
View File
@@ -0,0 +1,27 @@
// Sprung nach oben — erscheint erst, wenn man wirklich weit unten ist.
// Das Devlog-Archiv wird sehr lang; ohne das scrollt man ewig zurück.
import { useEffect, useState } from 'react';
import { IconChevronUp } from '../icons.jsx';
export default function ToTop({ after = 900 }) {
const [show, setShow] = useState(false);
useEffect(() => {
const onScroll = () => setShow(window.scrollY > after);
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, [after]);
if (!show) return null;
return (
<button
className="to-top"
aria-label="Nach oben"
title="Nach oben"
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
>
<IconChevronUp size={18} />
</button>
);
}