Web: Foto schon beim Anlegen eines Produkts hinterlegen

Bisher ging ein Foto erst nach dem Speichern (Produkt-ID noetig). Jetzt kann man
beim Neuanlegen direkt "Foto machen"/"Galerie" nutzen - das Bild wird vorgemerkt,
als Vorschau gezeigt und nach dem Anlegen hochgeladen. Nuetzlich, wenn Open Food/
Products Facts kein Bild liefert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-28 10:28:56 +02:00
parent 27f84003f3
commit 4141781ffb

View File

@@ -107,6 +107,9 @@ export default function ProductForm() {
const [bildVersion, setBildVersion] = useState(0);
// Ob gerade ein Bild vorliegt steuert, ob „Entfernen" angeboten wird.
const [hatBild, setHatBild] = useState(false);
// Beim Neuanlegen gibt es noch keine Produkt-ID zum Hochladen: Foto zwischen-
// speichern und nach dem Anlegen hochladen. { file, url } | null.
const [pendingPhoto, setPendingPhoto] = useState(null);
const selectedUnit = units.find((u) => String(u.id) === String(form.unit_id)) || null;
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
@@ -198,10 +201,18 @@ export default function ProductForm() {
async function onPickPhoto(e) {
const file = e.target.files?.[0];
e.target.value = ""; // erlaubt, dieselbe Datei erneut zu wählen
if (!file || isNew) return;
if (!file) return;
setError(null);
try {
const foto = await verkleinereBild(file);
if (isNew) {
// Noch keine ID Foto vormerken und erst nach dem Anlegen hochladen.
setPendingPhoto((alt) => {
if (alt?.url) URL.revokeObjectURL(alt.url);
return { file: foto, url: URL.createObjectURL(foto) };
});
return;
}
await api.uploadProductImage(id, foto);
setBildVersion((v) => v + 1);
toast("Foto gespeichert.");
@@ -210,6 +221,13 @@ export default function ProductForm() {
}
}
function removePendingPhoto() {
setPendingPhoto((alt) => {
if (alt?.url) URL.revokeObjectURL(alt.url);
return null;
});
}
async function removePhoto() {
const ok = await confirm({
title: "Foto entfernen?", confirmLabel: "Entfernen", danger: true,
@@ -507,6 +525,16 @@ export default function ProductForm() {
try {
if (isNew) {
const created = await api.createProduct(buildPayload());
// Vorgemerktes Foto jetzt hochladen ein Fehler dabei darf das Anlegen
// nicht zunichte machen, der Artikel existiert bereits.
if (pendingPhoto) {
try {
await api.uploadProductImage(created.id, pendingPhoto.file);
} catch (err) {
toast(`Artikel angelegt, aber Foto fehlgeschlagen: ${err.message}`, "warn");
}
removePendingPhoto();
}
navigate(`/products/${created.id}`);
} else {
await api.updateProduct(id, buildPayload());
@@ -601,9 +629,13 @@ export default function ProductForm() {
<input value={form.brand} onChange={(e) => set("brand", e.target.value)} disabled={readOnly} />
</label>
</div>
{!isNew && (
{(!isNew || isAdmin) && (
<div className="produkt-foto">
<ProduktBild productId={id} alt={form.name} version={bildVersion} onLoaded={setHatBild} />
{isNew
? pendingPhoto && (
<img className="produkt-bild" src={pendingPhoto.url} alt={form.name || ""} />
)
: <ProduktBild productId={id} alt={form.name} version={bildVersion} onLoaded={setHatBild} />}
{isAdmin && (
<div className="foto-knoepfe">
<label className="btn sm" title="Mit der Kamera aufnehmen">
@@ -614,9 +646,13 @@ export default function ProductForm() {
Galerie
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
</label>
{hatBild && (
<button type="button" className="btn sm" onClick={removePhoto}>Entfernen</button>
)}
{isNew
? pendingPhoto && (
<button type="button" className="btn sm" onClick={removePendingPhoto}>Entfernen</button>
)
: hatBild && (
<button type="button" className="btn sm" onClick={removePhoto}>Entfernen</button>
)}
</div>
)}
</div>