Files
vereinskalender/components/AdminSystemSettings.tsx
2026-01-15 16:24:09 +01:00

203 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useState } from "react";
export default function AdminSystemSettings() {
const [apiKey, setApiKey] = useState("");
const [provider, setProvider] = useState("osm");
const [registrationEnabled, setRegistrationEnabled] = useState(true);
const [logoFile, setLogoFile] = useState<File | null>(null);
const [logoVersion, setLogoVersion] = useState(() => Date.now());
const [hasLogo, setHasLogo] = useState<boolean | null>(null);
const [status, setStatus] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const load = async () => {
try {
const response = await fetch("/api/settings/google-places");
if (!response.ok) {
throw new Error("Einstellungen konnten nicht geladen werden.");
}
const payload = await response.json();
setApiKey(payload.apiKey || "");
setProvider(payload.provider || "osm");
setRegistrationEnabled(payload.registrationEnabled !== false);
} catch (err) {
setError((err as Error).message);
}
};
const loadLogoStatus = async () => {
try {
const response = await fetch("/api/branding/logo", {
method: "HEAD",
cache: "no-store"
});
setHasLogo(response.ok);
} catch {
setHasLogo(false);
}
};
useEffect(() => {
load();
loadLogoStatus();
}, []);
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setStatus(null);
setError(null);
const response = await fetch("/api/settings/google-places", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ apiKey, provider, registrationEnabled })
});
if (!response.ok) {
const data = await response.json();
setError(data.error || "Speichern fehlgeschlagen.");
return;
}
setStatus("Gespeichert.");
};
const onLogoUpload = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setStatus(null);
setError(null);
if (!logoFile) {
setError("Bitte ein Logo auswählen.");
return;
}
const formData = new FormData();
formData.append("file", logoFile);
const response = await fetch("/api/settings/logo", {
method: "POST",
body: formData
});
if (!response.ok) {
const data = await response.json();
setError(data.error || "Logo-Upload fehlgeschlagen.");
return;
}
setLogoFile(null);
setLogoVersion(Date.now());
setHasLogo(true);
setStatus("Logo gespeichert.");
};
const onLogoRemove = async () => {
setStatus(null);
setError(null);
const response = await fetch("/api/settings/logo", { method: "DELETE" });
if (!response.ok) {
const data = await response.json();
setError(data.error || "Logo konnte nicht gelöscht werden.");
return;
}
setHasLogo(false);
setLogoVersion(Date.now());
setStatus("Logo entfernt.");
};
return (
<section className="card space-y-4">
<div>
<p className="text-xs uppercase tracking-[0.2em] text-slate-500">System</p>
<h1 className="text-2xl font-semibold">API Einstellungen</h1>
</div>
<form onSubmit={onLogoUpload} className="space-y-3">
<div>
<p className="text-sm font-medium text-slate-700">App-Logo</p>
<p className="text-xs text-slate-500">
PNG, JPG, WEBP oder SVG. Empfohlen: 240×64 px.
</p>
</div>
{hasLogo ? (
<div className="flex items-center gap-3">
<img
src={`/api/branding/logo?ts=${logoVersion}`}
alt="Aktuelles Logo"
className="h-10 max-w-[180px] rounded-md border border-slate-200 bg-white object-contain px-2"
onError={() => setHasLogo(false)}
/>
<button
type="button"
onClick={onLogoRemove}
className="btn-ghost"
>
Logo entfernen
</button>
</div>
) : (
<p className="text-sm text-slate-500">Kein Logo hinterlegt.</p>
)}
<div className="flex flex-wrap items-center gap-3">
<input
type="file"
accept="image/png,image/jpeg,image/webp,image/svg+xml"
onChange={(event) =>
setLogoFile(event.currentTarget.files?.[0] || null)
}
className="block text-sm text-slate-600"
/>
<button type="submit" className="btn-accent">
Logo hochladen
</button>
</div>
</form>
<form onSubmit={onSubmit} className="space-y-3">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-700">
Ortsanbieter
</label>
<select
value={provider}
onChange={(event) => setProvider(event.target.value)}
className="w-full rounded-xl border border-slate-300 px-3 py-2"
>
<option value="osm">OpenStreetMap (Nominatim)</option>
<option value="google">Google Places</option>
</select>
</div>
{provider === "google" && (
<div className="space-y-2">
<label className="text-sm font-medium text-slate-700">
Google Places API Key
</label>
<input
type="password"
value={apiKey}
onChange={(event) => setApiKey(event.target.value)}
className="w-full rounded-xl border border-slate-300 px-3 py-2"
placeholder="AIza..."
required
/>
</div>
)}
<label className="flex items-center gap-2 text-sm text-slate-700">
<input
type="checkbox"
checked={registrationEnabled}
onChange={(event) => setRegistrationEnabled(event.target.checked)}
/>
Registrierung erlauben
</label>
<button type="submit" className="btn-accent">
Speichern
</button>
</form>
{status && <p className="text-sm text-emerald-600">{status}</p>}
{error && <p className="text-sm text-red-600">{error}</p>}
</section>
);
}