Web: "Entfernen" nur zeigen, wenn ein Artikelbild vorliegt

Der Entfernen-Knopf stand auch ohne Bild da. ProduktBild meldet jetzt via
onLoaded, ob ein Bild geladen wurde; das Produktformular blendet "Entfernen"
entsprechend ein oder aus. "Foto machen" und "Galerie" bleiben immer sichtbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-26 12:28:02 +02:00
parent dbdd977bad
commit 2a507851d9
2 changed files with 26 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { authorizedObjectUrl } from "../api";
import Icon from "./Icon";
@@ -7,9 +7,16 @@ import Icon from "./Icon";
*
* Die Objekt-URL wird beim Verlassen wieder freigegeben, sonst hält der Browser
* jedes angesehene Bild bis zum Neuladen der Seite im Speicher.
*
* ``onLoaded`` meldet, ob ein Bild vorliegt so kann die Artikelseite z.B. den
* „Entfernen"-Knopf ausblenden, wenn es nichts zu entfernen gibt.
*/
function useBildUrl(productId, version = 0) {
function useBildUrl(productId, version = 0, onLoaded) {
const [url, setUrl] = useState(null);
// Über einen Ref, damit ein bei jedem Render neu erzeugtes onLoaded den Effekt
// nicht erneut auslöst.
const onLoadedRef = useRef(onLoaded);
onLoadedRef.current = onLoaded;
useEffect(() => {
if (!productId) return undefined;
@@ -20,15 +27,21 @@ function useBildUrl(productId, version = 0) {
// Speichern nicht seine zwischengespeicherte Fassung ausliefert.
authorizedObjectUrl(`/products/${productId}/image${version ? `?v=${version}` : ""}`)
.then((neu) => {
if (!neu) return;
if (abgebrochen) {
URL.revokeObjectURL(neu);
if (neu) URL.revokeObjectURL(neu);
return;
}
onLoadedRef.current?.(Boolean(neu));
if (!neu) {
setUrl(null);
return;
}
erzeugt = neu;
setUrl(neu);
})
.catch(() => {});
.catch(() => {
if (!abgebrochen) onLoadedRef.current?.(false);
});
return () => {
abgebrochen = true;
@@ -46,8 +59,8 @@ function useBildUrl(productId, version = 0) {
* Rendert nichts, solange kein Bild vorliegt ein leerer Rahmen neben einem
* Artikel, der nie ein Bild bekommen wird, wäre nur Lärm.
*/
export default function ProduktBild({ productId, alt, className = "", version = 0 }) {
const url = useBildUrl(productId, version);
export default function ProduktBild({ productId, alt, className = "", version = 0, onLoaded }) {
const url = useBildUrl(productId, version, onLoaded);
if (!url) return null;
return <img className={`produkt-bild ${className}`.trim()} src={url} alt={alt || ""} />;
}

View File

@@ -96,6 +96,8 @@ export default function ProductForm() {
const [offDaten, setOffDaten] = useState(null);
const [offBusy, setOffBusy] = useState(false);
const [bildVersion, setBildVersion] = useState(0);
// Ob gerade ein Bild vorliegt steuert, ob „Entfernen" angeboten wird.
const [hatBild, setHatBild] = useState(false);
const selectedUnit = units.find((u) => String(u.id) === String(form.unit_id)) || null;
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
@@ -561,7 +563,7 @@ export default function ProductForm() {
</div>
{!isNew && (
<div className="produkt-foto">
<ProduktBild productId={id} alt={form.name} version={bildVersion} />
<ProduktBild productId={id} alt={form.name} version={bildVersion} onLoaded={setHatBild} />
{isAdmin && (
<div className="foto-knoepfe">
<label className="btn sm" title="Mit der Kamera aufnehmen">
@@ -572,7 +574,9 @@ export default function ProductForm() {
Galerie
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
</label>
{hatBild && (
<button type="button" className="btn sm ghost" onClick={removePhoto}>Entfernen</button>
)}
</div>
)}
</div>