Redesign Webinterface: Voxel-Game-Ästhetik
- Selbst gehostete Fonts (Inter Variable, JetBrains Mono) — kein CDN - Voxel-Würfel-Logo (SVG), Grid-Hintergrund mit grünem Glow, Gradient-Hero - Devlogs als Timeline mit Voxel-Markern, Datums-Chips und Relativzeit - Commits als Zeilen-Liste mit SHA-, Repo- und Branch-Chips statt Tabelle - Discord-Login-Button mit Logo, User-Chip mit Avatar im Header - Mobile-Layout (Karten, umbrechende Commit-Zeilen) - Im Browser verifiziert: Desktop + Mobile, Devlogs + Commits (mit Admin-Session) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,16 @@ const dateFmt = new Intl.DateTimeFormat('de-DE', {
|
||||
weekday: 'long', day: '2-digit', month: 'long', year: 'numeric',
|
||||
});
|
||||
|
||||
/** Relative Angabe wie „vor 3 Tagen" für die Karten-Ecke */
|
||||
function relative(iso) {
|
||||
const days = Math.floor((Date.now() - new Date(iso).getTime()) / 86400000);
|
||||
if (days <= 0) return 'heute';
|
||||
if (days === 1) return 'gestern';
|
||||
if (days < 30) return `vor ${days} Tagen`;
|
||||
const months = Math.floor(days / 30);
|
||||
return months === 1 ? 'vor 1 Monat' : `vor ${months} Monaten`;
|
||||
}
|
||||
|
||||
export default function Devlogs() {
|
||||
const [data, setData] = useState(null);
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -14,27 +24,42 @@ export default function Devlogs() {
|
||||
useEffect(() => {
|
||||
setError(false);
|
||||
apiGet(`/api/devlogs?page=${page}`).then(setData).catch(() => setError(true));
|
||||
window.scrollTo(0, 0);
|
||||
}, [page]);
|
||||
|
||||
if (error) return <p className="notice">Devlogs konnten nicht geladen werden.</p>;
|
||||
if (!data) return <p className="notice">Lade …</p>;
|
||||
if (data.total === 0) return <p className="notice">Noch keine Devlogs archiviert.</p>;
|
||||
|
||||
const pages = Math.ceil(data.total / data.pageSize);
|
||||
const pages = data ? Math.ceil(data.total / data.pageSize) : 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Devlogs</h1>
|
||||
<p className="subtitle">{data.total} Einträge aus der Entwicklung von EcoGame</p>
|
||||
<section className="hero">
|
||||
<h1>Devlog</h1>
|
||||
<p>
|
||||
Die Entwicklung von <strong>EcoGame</strong> — einem Voxel-Sandbox-Survival
|
||||
in Unreal Engine 5. Frisch aus dem Editor, Tag für Tag.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{data.items.map((d) => (
|
||||
<article className="card" key={d.message_id}>
|
||||
<div className="card-date">{dateFmt.format(new Date(d.posted_at))}</div>
|
||||
<div className="card-body">
|
||||
<Markdown text={d.content} />
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
{error && <p className="notice">Devlogs konnten nicht geladen werden.</p>}
|
||||
{!error && !data && <p className="notice">Lade …</p>}
|
||||
{data?.total === 0 && <p className="notice">Noch keine Devlogs archiviert.</p>}
|
||||
|
||||
{data?.total > 0 && (
|
||||
<div className="timeline">
|
||||
{data.items.map((d) => (
|
||||
<article className="card" key={d.message_id}>
|
||||
<div className="card-meta">
|
||||
<span className="card-date">
|
||||
{dateFmt.format(new Date(d.posted_at))}
|
||||
</span>
|
||||
<span className="card-rel">{relative(d.posted_at)}</span>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<Markdown text={d.content} />
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pages > 1 && (
|
||||
<div className="pager">
|
||||
|
||||
Reference in New Issue
Block a user