Files
GerbilManager/gerbil-manager-web/src/pages/WebseitePage.tsx
Gulum c20e6b5426 WEB-0b: CMS-Verwaltung der öffentlichen Webseite (Hand-Editier-UI)
Neuer Bereich /webseite im Manager (Nav: "Mehr"-Blatt) zur Pflege der 6
Webseiten-Seiten gegen Pams WEB-0-Backend (LAN-only, keine Auth):

- Übersicht: alle Seiten mit Status (Entwurf/Veröffentlicht).
- Seiten-Editor: Titel + SEO-Beschreibung + Status speichern.
- Geordneter Block-Editor: Blöcke hinzufügen (Überschrift, Textabschnitt/
  Markdown, Bild, Galerie, Kontaktangaben), bearbeiten, nach oben/unten
  verschieben (PUT .../blocks/order), löschen. Die dynamische AbgabetiereList
  ist nicht manuell anlegbar; nur ihr Einleitungstext ist editierbar.

Vertraege: GET /pages, GET /pages/{slug}, PUT /pages/{id},
POST /pages/{pageId}/blocks, PUT /blocks/{id}, DELETE /blocks/{id},
PUT /pages/{pageId}/blocks/order. Enums als Strings (Draft/Published, Heading...).

Deutsch via de.ts (LF beibehalten), mobile-first. e2e-Mock um die CMS-Routen
erweitert (zustandsbehaftet) + Smoke (Block anlegen/bearbeiten/verschieben,
bleibt erhalten). Gate gruen: build, eslint, vitest 69, e2e 68.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 11:23:34 +02:00

67 lines
2.0 KiB
TypeScript

/** WEB-0b: Übersicht der Webseiten-Seiten mit Status (Entwurf/Veröffentlicht). */
import { Link } from 'react-router-dom'
import { de } from '../strings/de'
import { listPages, type PageStatus } from '../api/pages'
import { useApi } from '../hooks/useApi'
import './webseite.css'
function StatusBadge({ status }: { status: PageStatus }) {
const t = de.pages.webseite
const published = status === 'Published'
return (
<span className={published ? 'status-badge status-badge--published' : 'status-badge status-badge--draft'}>
{published ? t.statusPublished : t.statusDraft}
</span>
)
}
export default function WebseitePage() {
const t = de.pages.webseite
const pages = useApi(() => listPages(), [])
if (pages.loading) return <p className="muted">{de.common.loading}</p>
if (pages.error || !pages.data) {
return (
<section className="page">
<h2>{t.title}</h2>
<div className="alert alert--error">
<span>{pages.error}</span>
<button type="button" className="btn" onClick={pages.reload}>
{de.common.retry}
</button>
</div>
</section>
)
}
return (
<section className="page">
<header className="page-head">
<div>
<h2>{t.title}</h2>
<p className="muted">{t.intro}</p>
</div>
</header>
{pages.data.length === 0 ? (
<p className="muted">{t.empty}</p>
) : (
<ul className="card-list">
{pages.data.map((p) => (
<li key={p.id} className="gerbil-card webseite-card">
<span className="gerbil-card__name">{p.title}</span>
<span className="gerbil-card__meta">/{p.slug}</span>
<span className="webseite-card__actions">
<StatusBadge status={p.status} />
<Link to={`/webseite/${p.slug}`} className="btn btn--primary">
{t.edit}
</Link>
</span>
</li>
))}
</ul>
)}
</section>
)
}