From a41dfba9e683ef0aa5fd7d0c053202e6381d7558 Mon Sep 17 00:00:00 2001 From: ordinarthur <@arthurbarre.js@gmail.com> Date: Tue, 21 Apr 2026 14:29:15 +0200 Subject: [PATCH] fix(admin): resolve and display current product image Form state for the images array only holds the media ID, not the populated doc. Read images.0.image via useField, then fetch /api/media/ to resolve url+alt for display. On upload, add a new row via dispatchFields if the array is empty, otherwise update in place. Co-Authored-By: Claude Opus 4.7 --- .../src/components/admin/ImageUploadSlot.tsx | 70 +++++++++++++++---- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/nextjs/src/components/admin/ImageUploadSlot.tsx b/nextjs/src/components/admin/ImageUploadSlot.tsx index aaaa203..8547271 100644 --- a/nextjs/src/components/admin/ImageUploadSlot.tsx +++ b/nextjs/src/components/admin/ImageUploadSlot.tsx @@ -1,29 +1,58 @@ 'use client' -import { useField } from '@payloadcms/ui' -import { useRef, useState } from 'react' +import { useField, useForm } from '@payloadcms/ui' +import { useEffect, useRef, useState } from 'react' type MediaDoc = { id: number | string; url?: string | null; alt?: string | null } -type ImageEntry = { id?: string; image?: MediaDoc | string | null } | null | undefined type Props = { displayName?: string } -function resolveFirstImage(entries: ImageEntry[] | undefined | null): { url: string; alt: string } { - const first = entries?.[0]?.image - if (!first) return { url: '', alt: '' } - if (typeof first === 'string') return { url: '', alt: '' } - return { url: first.url ?? '', alt: first.alt ?? '' } +function extractMediaId(value: unknown): string | number | null { + if (value == null) return null + if (typeof value === 'number' || typeof value === 'string') return value + if (typeof value === 'object' && 'id' in (value as object)) { + return (value as { id: number | string }).id ?? null + } + return null } export function ImageUploadSlot({ displayName }: Props) { - const { value: images, setValue } = useField({ path: 'images' }) + const { value: mediaValue, setValue: setMedia } = useField({ path: 'images.0.image' }) + const { dispatchFields } = useForm() const fileInput = useRef(null) const [uploading, setUploading] = useState(false) const [error, setError] = useState(null) + const [resolved, setResolved] = useState(null) - const { url, alt } = resolveFirstImage(images) + const mediaId = extractMediaId(mediaValue) + + useEffect(() => { + let cancelled = false + if (mediaId == null) { + setResolved(null) + return + } + if (resolved && resolved.id === mediaId) return + if (mediaValue && typeof mediaValue === 'object' && 'url' in (mediaValue as object)) { + setResolved(mediaValue as MediaDoc) + return + } + ;(async () => { + try { + const res = await fetch(`/api/media/${mediaId}`, { credentials: 'include' }) + if (!res.ok) return + const doc = await res.json() + if (!cancelled) setResolved({ id: doc.id, url: doc.url, alt: doc.alt }) + } catch { + /* ignore */ + } + })() + return () => { + cancelled = true + } + }, [mediaId, mediaValue, resolved]) const onPick = () => fileInput.current?.click() @@ -40,9 +69,19 @@ export function ImageUploadSlot({ displayName }: Props) { const res = await fetch('/api/media', { method: 'POST', body: fd, credentials: 'include' }) if (!res.ok) throw new Error(`upload ${res.status}`) const { doc } = await res.json() - const media: MediaDoc = { id: doc.id, url: doc.url, alt: doc.alt } - const next = [{ image: media }, ...(images ?? []).slice(1)] - setValue(next) + if (mediaId == null) { + dispatchFields({ + type: 'ADD_ROW', + path: 'images', + rowIndex: 0, + subFieldState: { + image: { initialValue: doc.id, valid: true, value: doc.id }, + }, + }) + } else { + setMedia(doc.id) + } + setResolved({ id: doc.id, url: doc.url, alt: doc.alt }) } catch (err) { setError(err instanceof Error ? err.message : 'upload failed') } finally { @@ -50,10 +89,13 @@ export function ImageUploadSlot({ displayName }: Props) { } } + const url = resolved?.url ?? '' + const alt = resolved?.alt ?? displayName ?? 'Produit' + return (
{url ? ( - {alt + {alt} ) : (
Cliquez pour
uploader une image