Release-Ankündigungen + öffentliche Changelog-Seite
- Gitea-Release-Webhook: Releases werden archiviert (releases-Tabelle) und neue 'published'-Releases als oranges 🚀-Embed mit Buttons angekündigt - Release-Kanal auf der Setup-Seite wählbar (leer = Feature aus) + Test-Button - /changelog: öffentliche Seite mit Tag-Chips, Pre-Release-Badge, Release-Notes - /api/releases öffentlich; Updates/Redeliveries posten nicht doppelt Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Routes, Route, NavLink, Navigate } from 'react-router-dom';
|
||||
import { apiGet } from './api.js';
|
||||
import Devlogs from './pages/Devlogs.jsx';
|
||||
import Changelog from './pages/Changelog.jsx';
|
||||
import Commits from './pages/Commits.jsx';
|
||||
import Settings from './pages/Settings.jsx';
|
||||
|
||||
@@ -69,6 +70,7 @@ export default function App() {
|
||||
|
||||
<nav className="nav-links">
|
||||
<NavLink to="/devlogs">Devlog</NavLink>
|
||||
<NavLink to="/changelog">Changelog</NavLink>
|
||||
{me.admin && <NavLink to="/commits">Commits</NavLink>}
|
||||
{me.admin && <NavLink to="/settings">Setup</NavLink>}
|
||||
</nav>
|
||||
@@ -92,6 +94,7 @@ export default function App() {
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/devlogs" replace />} />
|
||||
<Route path="/devlogs" element={<Devlogs me={me} />} />
|
||||
<Route path="/changelog" element={<Changelog />} />
|
||||
<Route path="/commits" element={<Commits me={me} />} />
|
||||
<Route path="/settings" element={<Settings me={me} />} />
|
||||
<Route path="*" element={<Navigate to="/devlogs" replace />} />
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { apiGet } from '../api.js';
|
||||
import { Markdown } from '../markdown.jsx';
|
||||
|
||||
const dateFmt = new Intl.DateTimeFormat('de-DE', {
|
||||
day: '2-digit', month: 'long', year: 'numeric',
|
||||
});
|
||||
|
||||
export default function Changelog() {
|
||||
const [data, setData] = useState(null);
|
||||
const [page, setPage] = useState(1);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setError(false);
|
||||
apiGet(`/api/releases?page=${page}`).then(setData).catch(() => setError(true));
|
||||
window.scrollTo(0, 0);
|
||||
}, [page]);
|
||||
|
||||
const pages = data ? Math.ceil(data.total / data.pageSize) : 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="page-header">
|
||||
<div className="page-bg-text">CHANGELOG</div>
|
||||
<div className="page-header-grid" aria-hidden="true" />
|
||||
<div className="page-header-content">
|
||||
<div className="page-tag">// Releases</div>
|
||||
<h1 className="page-title">Changelog</h1>
|
||||
<p className="page-subtitle">Alle Versionen und Release-Notes.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="content">
|
||||
{error && <p className="notice">Changelog konnte nicht geladen werden.</p>}
|
||||
{!error && !data && <p className="notice">Lade …</p>}
|
||||
{data?.total === 0 && <p className="notice">Noch keine Releases veröffentlicht.</p>}
|
||||
|
||||
{data?.total > 0 &&
|
||||
data.items.map((r) => (
|
||||
<article className="card release-card" key={`${r.repo}/${r.tag}`}>
|
||||
<div className="card-meta">
|
||||
<span className="release-tag">{r.tag}</span>
|
||||
{r.prerelease ? <span className="chip chip-branch">Pre-Release</span> : null}
|
||||
<span className="chip chip-repo">{r.repo.split('/').pop()}</span>
|
||||
<span className="card-rel">{dateFmt.format(new Date(r.published_at))}</span>
|
||||
</div>
|
||||
{r.name && r.name !== r.tag && <h3 className="release-name">{r.name}</h3>}
|
||||
<div className="card-body">
|
||||
<Markdown text={r.body || '*Keine Release-Notes.*'} />
|
||||
</div>
|
||||
{r.url && (
|
||||
<div className="card-footer">
|
||||
<a href={r.url} target="_blank" rel="noreferrer">Release in Gitea ansehen →</a>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
|
||||
{pages > 1 && (
|
||||
<div className="pager">
|
||||
<button className="btn" disabled={page <= 1} onClick={() => setPage(page - 1)}>
|
||||
← Neuere
|
||||
</button>
|
||||
<span className="pager-info">
|
||||
{String(page).padStart(2, '0')} / {String(pages).padStart(2, '0')}
|
||||
</span>
|
||||
<button className="btn" disabled={page >= pages} onClick={() => setPage(page + 1)}>
|
||||
Ältere →
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -136,6 +136,20 @@ export default function Settings({ me }) {
|
||||
<button className="btn" onClick={() => sendTest('commit')}>Test senden</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label>Release-Kanal <span className="field-hint">Ankündigungen bei neuen Gitea-Releases — leer = aus</span></label>
|
||||
<div className="field-row">
|
||||
<select
|
||||
value={form.release_channel_id ?? ''}
|
||||
onChange={(e) => setForm({ ...form, release_channel_id: e.target.value })}
|
||||
>
|
||||
<option value="">— deaktiviert —</option>
|
||||
{channelOptions}
|
||||
</select>
|
||||
<button className="btn" onClick={() => sendTest('release')}>Test senden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Commit-Feed */}
|
||||
|
||||
+18
-1
@@ -238,7 +238,7 @@ body::after {
|
||||
transition: border-color .25s, background .25s;
|
||||
}
|
||||
.card:hover { border-color: var(--border); background: var(--bg3); }
|
||||
.card::before {
|
||||
.timeline .card::before {
|
||||
/* Marker auf der Timeline */
|
||||
content: '';
|
||||
position: absolute; left: -1.72rem; top: 1.55rem;
|
||||
@@ -337,6 +337,23 @@ body::after {
|
||||
.card-footer a { color: var(--muted); border-bottom: 1px solid transparent; }
|
||||
.card-footer a:hover { color: var(--neon); }
|
||||
|
||||
/* ── CHANGELOG ─────────────────────────────────────── */
|
||||
.release-card { margin-bottom: 1.5rem; }
|
||||
.release-tag {
|
||||
font-family: var(--mono);
|
||||
font-size: .85rem; letter-spacing: .1em;
|
||||
color: var(--neon);
|
||||
border: 1px solid var(--border);
|
||||
background: rgba(245, 197, 24, .06);
|
||||
padding: .12rem .6rem;
|
||||
}
|
||||
.release-name {
|
||||
font-family: var(--display);
|
||||
font-size: 1.35rem; letter-spacing: .03em;
|
||||
margin: .3rem 0 .2rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* ── COMMIT-LISTE ──────────────────────────────────── */
|
||||
.commit-list { border: 1px solid rgba(255, 255, 255, .06); background: var(--bg2); }
|
||||
.commit-row {
|
||||
|
||||
Reference in New Issue
Block a user