110 lines
6.4 KiB
TypeScript
110 lines
6.4 KiB
TypeScript
'use client'
|
|
import { useState, useEffect } from 'react'
|
|
|
|
interface Stat { id: string; wert: string; label: string; reihenfolge: number }
|
|
|
|
const inp: React.CSSProperties = { width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid var(--border-color)', background: 'var(--bg)', color: 'var(--text-primary)', fontSize: '14px', boxSizing: 'border-box' }
|
|
const lbl: React.CSSProperties = { fontSize: '11px', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '1px', color: 'var(--text-muted)', display: 'block', marginBottom: '6px' }
|
|
|
|
export function UeberUnsVerwaltung() {
|
|
const [eyebrowText, setEyebrowText] = useState('Klein, aber fein')
|
|
const [absatz1, setAbsatz1] = useState('')
|
|
const [absatz2, setAbsatz2] = useState('')
|
|
const [bildUrl, setBildUrl] = useState<string | null>(null)
|
|
const [stats, setStats] = useState<Stat[]>([])
|
|
const [newStat, setNewStat] = useState({ wert: '', label: '' })
|
|
const [saving, setSaving] = useState(false)
|
|
const [saved, setSaved] = useState(false)
|
|
const [uploading, setUploading] = useState(false)
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
fetch('/api/admin/ueber-uns').then(r => r.json()).then(({ content: c, stats: s }) => {
|
|
if (c) { setEyebrowText(c.eyebrow_text ?? 'Klein, aber fein'); setAbsatz1(c.absatz1 ?? ''); setAbsatz2(c.absatz2 ?? ''); setBildUrl(c.bild_url ?? null) }
|
|
setStats(s ?? [])
|
|
setLoading(false)
|
|
})
|
|
}, [])
|
|
|
|
async function handleSave(e: React.FormEvent) {
|
|
e.preventDefault(); setSaving(true)
|
|
await fetch('/api/admin/ueber-uns', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ eyebrow_text: eyebrowText, absatz1, absatz2, bild_url: bildUrl }) })
|
|
setSaving(false); setSaved(true); setTimeout(() => setSaved(false), 2000)
|
|
}
|
|
|
|
async function handleBildUpload(e: React.ChangeEvent<HTMLInputElement>) {
|
|
const file = e.target.files?.[0]
|
|
if (!file) return
|
|
setUploading(true)
|
|
const fd = new FormData(); fd.append('file', file)
|
|
const res = await fetch('/api/admin/ueber-uns/upload', { method: 'POST', body: fd })
|
|
const { url, error } = await res.json()
|
|
if (url) setBildUrl(url)
|
|
else alert(error ?? 'Upload fehlgeschlagen')
|
|
setUploading(false)
|
|
}
|
|
|
|
async function addStat() {
|
|
if (!newStat.wert.trim() || !newStat.label.trim()) return
|
|
const res = await fetch('/api/admin/ueber-uns/stats', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...newStat, reihenfolge: stats.length }) })
|
|
const { stat } = await res.json()
|
|
setStats(prev => [...prev, stat]); setNewStat({ wert: '', label: '' })
|
|
}
|
|
|
|
async function deleteStat(id: string) {
|
|
await fetch(`/api/admin/ueber-uns/stats/${id}`, { method: 'DELETE' })
|
|
setStats(prev => prev.filter(s => s.id !== id))
|
|
}
|
|
|
|
if (loading) return <p style={{ color: 'var(--text-muted)' }}>Lade…</p>
|
|
|
|
return (
|
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '32px' }}>
|
|
<form onSubmit={handleSave} style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
<div>
|
|
<label style={lbl}>Eyebrow-Text</label>
|
|
<input style={inp} value={eyebrowText} onChange={e => setEyebrowText(e.target.value)} placeholder="z. B. Klein, aber fein" />
|
|
</div>
|
|
<div>
|
|
<label style={lbl}>Absatz 1</label>
|
|
<textarea rows={4} style={{ ...inp, resize: 'vertical' }} value={absatz1} onChange={e => setAbsatz1(e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label style={lbl}>Absatz 2</label>
|
|
<textarea rows={4} style={{ ...inp, resize: 'vertical' }} value={absatz2} onChange={e => setAbsatz2(e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label style={lbl}>Sektionsbild</label>
|
|
{bildUrl && (
|
|
/* eslint-disable-next-line @next/next/no-img-element */
|
|
<img src={bildUrl} alt="Vorschau" style={{ width: '100%', height: '120px', objectFit: 'cover', borderRadius: '6px', marginBottom: '8px', border: '1px solid var(--border-color)' }} />
|
|
)}
|
|
<input type="file" accept="image/*" onChange={handleBildUpload} disabled={uploading} style={{ fontSize: '13px', color: 'var(--text-muted)' }} />
|
|
{uploading && <p style={{ fontSize: '12px', color: 'var(--text-muted)', marginTop: '4px' }}>Hochladen…</p>}
|
|
</div>
|
|
<button type="submit" disabled={saving} style={{ padding: '10px', borderRadius: '6px', background: 'var(--accent)', color: '#fff', fontWeight: 700, border: 'none', cursor: 'pointer', fontSize: '14px' }}>
|
|
{saving ? 'Speichert…' : saved ? '✓ Gespeichert' : 'Speichern'}
|
|
</button>
|
|
</form>
|
|
|
|
<div>
|
|
<label style={lbl}>Statistiken</label>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '12px' }}>
|
|
{stats.map(s => (
|
|
<div key={s.id} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto', gap: '8px', alignItems: 'center' }}>
|
|
<input style={inp} defaultValue={s.wert} onBlur={async e => { await fetch(`/api/admin/ueber-uns/stats/${s.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ wert: e.target.value }) }) }} />
|
|
<input style={inp} defaultValue={s.label} onBlur={async e => { await fetch(`/api/admin/ueber-uns/stats/${s.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ label: e.target.value }) }) }} />
|
|
<button onClick={() => deleteStat(s.id)} style={{ padding: '6px 10px', borderRadius: '6px', background: 'transparent', border: '1px solid rgba(220,38,38,0.3)', color: '#f87171', cursor: 'pointer', fontSize: '12px' }}>✕</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto', gap: '8px', alignItems: 'center' }}>
|
|
<input style={inp} value={newStat.wert} onChange={e => setNewStat(p => ({ ...p, wert: e.target.value }))} placeholder="Wert (z. B. 2022)" />
|
|
<input style={inp} value={newStat.label} onChange={e => setNewStat(p => ({ ...p, label: e.target.value }))} placeholder="Label (z. B. Gegründet)" />
|
|
<button type="button" onClick={addStat} style={{ padding: '8px 14px', borderRadius: '6px', background: 'var(--accent)', color: '#fff', fontWeight: 700, border: 'none', cursor: 'pointer', fontSize: '13px' }}>+</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|