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:
@@ -107,6 +107,9 @@ export default function ProductForm() {
|
|||||||
const [bildVersion, setBildVersion] = useState(0);
|
const [bildVersion, setBildVersion] = useState(0);
|
||||||
// Ob gerade ein Bild vorliegt – steuert, ob „Entfernen" angeboten wird.
|
// Ob gerade ein Bild vorliegt – steuert, ob „Entfernen" angeboten wird.
|
||||||
const [hatBild, setHatBild] = useState(false);
|
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 selectedUnit = units.find((u) => String(u.id) === String(form.unit_id)) || null;
|
||||||
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
|
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
|
||||||
@@ -198,10 +201,18 @@ export default function ProductForm() {
|
|||||||
async function onPickPhoto(e) {
|
async function onPickPhoto(e) {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
e.target.value = ""; // erlaubt, dieselbe Datei erneut zu wählen
|
e.target.value = ""; // erlaubt, dieselbe Datei erneut zu wählen
|
||||||
if (!file || isNew) return;
|
if (!file) return;
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const foto = await verkleinereBild(file);
|
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);
|
await api.uploadProductImage(id, foto);
|
||||||
setBildVersion((v) => v + 1);
|
setBildVersion((v) => v + 1);
|
||||||
toast("Foto gespeichert.");
|
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() {
|
async function removePhoto() {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: "Foto entfernen?", confirmLabel: "Entfernen", danger: true,
|
title: "Foto entfernen?", confirmLabel: "Entfernen", danger: true,
|
||||||
@@ -507,6 +525,16 @@ export default function ProductForm() {
|
|||||||
try {
|
try {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
const created = await api.createProduct(buildPayload());
|
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}`);
|
navigate(`/products/${created.id}`);
|
||||||
} else {
|
} else {
|
||||||
await api.updateProduct(id, buildPayload());
|
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} />
|
<input value={form.brand} onChange={(e) => set("brand", e.target.value)} disabled={readOnly} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{!isNew && (
|
{(!isNew || isAdmin) && (
|
||||||
<div className="produkt-foto">
|
<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 && (
|
{isAdmin && (
|
||||||
<div className="foto-knoepfe">
|
<div className="foto-knoepfe">
|
||||||
<label className="btn sm" title="Mit der Kamera aufnehmen">
|
<label className="btn sm" title="Mit der Kamera aufnehmen">
|
||||||
@@ -614,7 +646,11 @@ export default function ProductForm() {
|
|||||||
Galerie
|
Galerie
|
||||||
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
|
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
|
||||||
</label>
|
</label>
|
||||||
{hatBild && (
|
{isNew
|
||||||
|
? pendingPhoto && (
|
||||||
|
<button type="button" className="btn sm" onClick={removePendingPhoto}>Entfernen</button>
|
||||||
|
)
|
||||||
|
: hatBild && (
|
||||||
<button type="button" className="btn sm" onClick={removePhoto}>Entfernen</button>
|
<button type="button" className="btn sm" onClick={removePhoto}>Entfernen</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user