From 2cfff02b69acab6fe5a299408f4698f3b4269ac2 Mon Sep 17 00:00:00 2001 From: Wira Irawan Date: Sun, 31 May 2026 10:47:55 +0700 Subject: [PATCH] Fix edit product images and warranty toggle --- .../products/[productId]/edit/page.tsx | 119 +++++++++++++++--- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/src/app/(dashboard)/products/[productId]/edit/page.tsx b/src/app/(dashboard)/products/[productId]/edit/page.tsx index ba2efa5..550da5a 100644 --- a/src/app/(dashboard)/products/[productId]/edit/page.tsx +++ b/src/app/(dashboard)/products/[productId]/edit/page.tsx @@ -7,6 +7,7 @@ import { useLanguage } from "@/lib/i18n-context"; import { getBackendErrorMessage } from "@/lib/error-message"; import { assertValidAddProductPayload } from "@/lib/product-request-validation"; import { assertUploadFileSize } from "@/lib/upload-limits"; +import { resolveBackendImageUrlFromValue } from "@/lib/image-url"; // ─── Types ──────────────────────────────────────────────────────────────────── @@ -108,6 +109,7 @@ interface EditState { fileId: string; }; warrantyInformation: { + enabled: boolean; type: string; duration: string; durationType: string; @@ -351,6 +353,9 @@ function newModel(index: number): EditModel { function apiToEditState(data: ApiProduct): EditState { const rawModels = Array.isArray(data?.productModels) ? data.productModels : []; + const warrantyType = toStr(data?.warrantyInformation?.type); + const warrantyDuration = toStr(data?.warrantyInformation?.duration); + const hasWarranty = Boolean(warrantyType.trim() || warrantyDuration.trim()); const models: EditModel[] = rawModels.length > 0 ? rawModels.map((m: ApiModel, i: number) => ({ @@ -452,8 +457,9 @@ function apiToEditState(data: ApiProduct): EditState { fileId: toStr(data?.complianceInformation?.fileId ?? data?.complianceInformation?.file), }, warrantyInformation: { - type: toStr(data?.warrantyInformation?.type), - duration: toStr(data?.warrantyInformation?.duration), + enabled: hasWarranty, + type: warrantyType, + duration: warrantyDuration, durationType: toStr(data?.warrantyInformation?.durationType) || "MONTH", }, }; @@ -491,10 +497,24 @@ function ImageSlotUpload({ const inputRef = useRef(null); const [uploading, setUploading] = useState(false); const [error, setError] = useState(""); + const [previewUrl, setPreviewUrl] = useState(""); + const [displayUrl, setDisplayUrl] = useState(""); + const fallbackUrl = fileId && !fileId.startsWith("http") + ? resolveBackendImageUrlFromValue(fileId, false) + : ""; + + useEffect(() => { + setDisplayUrl(previewUrl || resolveBackendImageUrlFromValue(fileId, true)); + }, [fileId, previewUrl]); async function handleChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; + const objectUrl = URL.createObjectURL(file); + setPreviewUrl((current) => { + if (current) URL.revokeObjectURL(current); + return objectUrl; + }); setUploading(true); setError(""); try { @@ -502,6 +522,8 @@ function ImageSlotUpload({ onUploaded(fileId); } catch (err) { setError(err instanceof Error ? err.message : "Upload gagal"); + URL.revokeObjectURL(objectUrl); + setPreviewUrl(""); } finally { setUploading(false); if (inputRef.current) inputRef.current.value = ""; @@ -510,10 +532,25 @@ function ImageSlotUpload({ return (
-
- - {fileId ? "image" : "add_photo_alternate"} - +
+ {displayUrl ? ( + { + if (fallbackUrl && displayUrl !== fallbackUrl) { + setDisplayUrl(fallbackUrl); + } else { + setDisplayUrl(""); + } + }} + /> + ) : ( + + {fileId ? "image" : "add_photo_alternate"} + + )}

{label}

@@ -542,12 +579,23 @@ function ModelImageUpload({ value, onUploaded }: { value: string; onUploaded: (i const [uploading, setUploading] = useState(false); const [error, setError] = useState(""); const [previewUrl, setPreviewUrl] = useState(""); + const [displayUrl, setDisplayUrl] = useState(""); + const fallbackUrl = value && !value.startsWith("http") + ? resolveBackendImageUrlFromValue(value, false) + : ""; + + useEffect(() => { + setDisplayUrl(previewUrl || resolveBackendImageUrlFromValue(value, true)); + }, [previewUrl, value]); async function handleChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; const objectUrl = URL.createObjectURL(file); - setPreviewUrl(objectUrl); + setPreviewUrl((current) => { + if (current) URL.revokeObjectURL(current); + return objectUrl; + }); setUploading(true); setError(""); try { @@ -555,6 +603,7 @@ function ModelImageUpload({ value, onUploaded }: { value: string; onUploaded: (i onUploaded(fileId); } catch (err) { setError(err instanceof Error ? err.message : "Upload gagal"); + URL.revokeObjectURL(objectUrl); setPreviewUrl(""); } finally { setUploading(false); @@ -572,8 +621,19 @@ function ModelImageUpload({ value, onUploaded }: { value: string; onUploaded: (i {hasImage ? ( <>
- {previewUrl ? ( - Model preview + {displayUrl ? ( + Model preview { + if (fallbackUrl && displayUrl !== fallbackUrl) { + setDisplayUrl(fallbackUrl); + } else { + setDisplayUrl(""); + } + }} + /> ) : (
image @@ -1343,7 +1403,17 @@ function EditProductPageInner() { productInformations: form.productInformations.filter((i) => i.paramName && i.paramValue), categoryInformations: form.categoryInformations.filter((i) => i.paramName && i.paramValue), complianceInformation: { ...form.complianceInformation }, - warrantyInformation: { ...form.warrantyInformation, duration: toNum(form.warrantyInformation.duration) }, + warrantyInformation: form.warrantyInformation.enabled + ? { + type: form.warrantyInformation.type, + duration: toNum(form.warrantyInformation.duration), + durationType: form.warrantyInformation.durationType, + } + : { + type: null, + duration: null, + durationType: "MONTH", + }, state: resolvedState, }; return base; @@ -1844,22 +1914,43 @@ function EditProductPageInner() { {/* Warranty */}
-

{e.warranty}

+
+

{e.warranty}

+ +
+
update({ warrantyInformation: { ...form.warrantyInformation, type: ev.target.value } })} - placeholder={e.warrantyType} className={inputCls} /> + placeholder={e.warrantyType} disabled={!form.warrantyInformation.enabled} className={inputCls} />
update({ warrantyInformation: { ...form.warrantyInformation, duration: ev.target.value } })} - placeholder={e.warrantyDuration} type="number" min="0" className={inputCls} /> + placeholder={e.warrantyDuration} type="number" min="0" disabled={!form.warrantyInformation.enabled} className={inputCls} />
+