Logo-Upload sichtbar machen; empfohlene Abmessungen angeben

Fehler: Ein hochgeladenes Logo blieb unsichtbar, auch nach dem Neuladen.
Die Oberflaeche prueft per Anfrage, ob ein Bild hinterlegt ist, und nutzte dafuer
HEAD. FastAPI registriert fuer eine GET-Route aber - anders als Starlette - kein
HEAD; die Anfrage lief in ein 405, und jedes hinterlegte Bild galt als "nicht
vorhanden". Der Upload selbst war die ganze Zeit in Ordnung.

- Neuer Endpunkt GET /branding liefert nur den Status ({"logo": true, ...}),
  ohne Bilddaten zu uebertragen. Die Oberflaeche fragt jetzt diesen ab.
- Die Bild-Route beantwortet zusaetzlich HEAD, damit sie sich erwartungsgemaess
  verhaelt.

Ausserdem:
- Die empfohlenen Abmessungen stehen jetzt sichtbar ueber der Vorschau
  (Logo etwa 400x72 px, Favicon 64x64 px, jeweils hoechstens 512 KB).
- Nach dem Hochladen wird die tatsaechliche Bildgroesse angezeigt, damit man
  sie mit der Empfehlung vergleichen kann.

Geprueft: "npm run build" laeuft durch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-23 08:55:17 +02:00
parent 903392e953
commit 4448718d8d
4 changed files with 54 additions and 12 deletions

View File

@@ -7,11 +7,13 @@ import Icon from "./Icon";
* Ein Bild der Installation setzen oder wieder entfernen (Logo bzw. Favicon).
* Ohne eigenes Bild bleibt das eingebaute Zeichen der Oberfläche stehen.
*/
export default function BrandingUpload({ kind, title, hint, onChanged }) {
export default function BrandingUpload({ kind, title, hint, empfehlung, onChanged }) {
const [vorhanden, setVorhanden] = useState(false);
const [version, setVersion] = useState(0);
const [busy, setBusy] = useState(false);
const [fehler, setFehler] = useState(null);
// Tatsaechliche Bildgroesse - damit man sieht, ob sie zur Empfehlung passt.
const [groesse, setGroesse] = useState(null);
const dateiFeld = useRef(null);
useEffect(() => {
@@ -55,13 +57,23 @@ export default function BrandingUpload({ kind, title, hint, onChanged }) {
return (
<div style={{ marginBottom: "var(--sp-4)" }}>
<label style={{ marginBottom: "var(--sp-2)" }}>{title}</label>
{empfehlung && (
<p className="muted small mt-0"><strong>Empfohlen:</strong> {empfehlung}</p>
)}
<div className="brand-preview">
{vorhanden ? (
<img src={brandingUrl(kind, version)} alt={title} />
<img src={brandingUrl(kind, version)} alt={title}
onLoad={(e) => setGroesse({
w: e.currentTarget.naturalWidth,
h: e.currentTarget.naturalHeight,
})} />
) : (
<span className="muted small">Kein eigenes Bild Standard wird verwendet</span>
)}
</div>
{vorhanden && groesse?.w > 0 && (
<p className="muted small mt-0">Hochgeladen: {groesse.w} × {groesse.h} px</p>
)}
<p className="muted small mt-0">{hint}</p>
{fehler && <div className="alert error"><Icon name="alert" size={16} />{fehler}</div>}
<div className="field-inline">