From 407f1474b1ea9d2d5540bbb19b9eda2c7365cb45 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Tue, 14 Apr 2026 11:29:43 +0700 Subject: [PATCH] feat: fixing conten website --- components/main/content-website.tsx | 316 +++++++++++++--------------- service/media-library.ts | 14 ++ 2 files changed, 157 insertions(+), 173 deletions(-) diff --git a/components/main/content-website.tsx b/components/main/content-website.tsx index 90dc169..a74816f 100644 --- a/components/main/content-website.tsx +++ b/components/main/content-website.tsx @@ -68,6 +68,10 @@ import { uploadPartnerLogo, } from "@/service/cms-landing"; import { submitCmsContentSubmission } from "@/service/cms-content-submissions"; +import { + parseMediaLibraryUploadPublicUrl, + uploadMediaLibraryFile, +} from "@/service/media-library"; function revokeBlobRef(ref: MutableRefObject) { if (ref.current) { @@ -88,6 +92,26 @@ function setPickedFile( setter(file); } +/** Upload to Media Library; returns public URL for CMS submission payloads (contributor). */ +async function uploadContributorAssetToPublicUrl( + file: File, +): Promise { + const fd = new FormData(); + fd.append("file", file); + const res = await uploadMediaLibraryFile(fd); + const url = parseMediaLibraryUploadPublicUrl(res); + if (url) return url; + await Swal.fire({ + icon: "error", + title: "Unggah berkas gagal", + text: String( + (res as { message?: unknown })?.message ?? + "Periksa koneksi dan akses Media Library, lalu coba lagi.", + ), + }); + return null; +} + type ContentWebsiteProps = { /** User level 2: changes go through approval instead of live CMS APIs. */ contributorMode?: boolean; @@ -303,16 +327,14 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (heroPendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL gambar", - text: "Sebagai kontributor, unggah file tidak didukung. Salin URL dari Media Library ke bidang Image URL.", - }); - return; - } setSaving(true); try { + let imageUrl = (heroRemoteUrl ?? "").trim(); + if (heroPendingFile) { + const up = await uploadContributorAssetToPublicUrl(heroPendingFile); + if (!up) return; + imageUrl = up; + } const res = await submitCmsContentSubmission({ domain: "hero", title: `Hero: ${heroPrimary.slice(0, 80)}`, @@ -324,7 +346,7 @@ export default function ContentWebsite({ description: heroDesc, primary_cta: heroCta1, secondary_cta_text: heroCta2, - image_url: (heroRemoteUrl ?? "").trim(), + image_url: imageUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -335,6 +357,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(heroBlobUrlRef); + setHeroPendingFile(null); + if (imageUrl) setHeroRemoteUrl(imageUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -407,16 +432,14 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (aboutPendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL media", - text: "Sebagai kontributor, unggah file tidak didukung. Gunakan URL dari Media Library.", - }); - return; - } setSaving(true); try { + let mediaUrl = (aboutRemoteMediaUrl ?? "").trim(); + if (aboutPendingFile) { + const up = await uploadContributorAssetToPublicUrl(aboutPendingFile); + if (!up) return; + mediaUrl = up; + } const res = await submitCmsContentSubmission({ domain: "about", title: `About: ${aboutPrimary.slice(0, 80)}`, @@ -428,7 +451,7 @@ export default function ContentWebsite({ description: aboutDesc, primary_cta: aboutCta1, secondary_cta_text: aboutCta2, - media_url: (aboutRemoteMediaUrl ?? "").trim(), + media_url: mediaUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -439,6 +462,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(aboutBlobUrlRef); + setAboutPendingFile(null); + if (mediaUrl) setAboutRemoteMediaUrl(mediaUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -545,16 +571,14 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (productPendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL gambar", - text: "Sebagai kontributor, gunakan URL dari Media Library untuk gambar produk.", - }); - return; - } setSaving(true); try { + let imageUrl = (productRemoteUrl ?? "").trim(); + if (productPendingFile) { + const up = await uploadContributorAssetToPublicUrl(productPendingFile); + if (!up) return; + imageUrl = up; + } const res = await submitCmsContentSubmission({ domain: "product", title: `Product: ${productPrimary.slice(0, 80)}`, @@ -565,7 +589,7 @@ export default function ContentWebsite({ secondary_title: productSecondary, description: productDesc, link_url: productLinkUrl, - image_url: (productRemoteUrl ?? "").trim(), + image_url: imageUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -576,6 +600,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(productBlobUrlRef); + setProductPendingFile(null); + if (imageUrl) setProductRemoteUrl(imageUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -736,16 +763,14 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (servicePendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL gambar", - text: "Sebagai kontributor, gunakan URL dari Media Library untuk gambar layanan.", - }); - return; - } setSaving(true); try { + let imageUrl = (serviceRemoteUrl ?? "").trim(); + if (servicePendingFile) { + const up = await uploadContributorAssetToPublicUrl(servicePendingFile); + if (!up) return; + imageUrl = up; + } const res = await submitCmsContentSubmission({ domain: "service", title: `Service: ${servicePrimary.slice(0, 80)}`, @@ -756,7 +781,7 @@ export default function ContentWebsite({ secondary_title: serviceSecondary, description: serviceDesc, link_url: serviceLinkUrl, - image_url: (serviceRemoteUrl ?? "").trim(), + image_url: imageUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -767,6 +792,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(serviceBlobUrlRef); + setServicePendingFile(null); + if (imageUrl) setServiceRemoteUrl(imageUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -919,24 +947,22 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (partnerPendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL logo", - text: "Sebagai kontributor, gunakan URL logo dari Media Library (isi URL gambar).", - }); - return; - } setSaving(true); try { + let imageUrl = (partnerRemoteUrl ?? "").trim(); + if (partnerPendingFile) { + const up = await uploadContributorAssetToPublicUrl(partnerPendingFile); + if (!up) return; + imageUrl = up; + } const res = await submitCmsContentSubmission({ domain: "partner", title: `Partner: ${partnerTitle.slice(0, 80)}`, payload: { partner_id: editingPartnerId ?? "", primary_title: partnerTitle.trim(), - image_path: partnerStoredPath, - image_url: (partnerRemoteUrl ?? "").trim(), + image_path: partnerPendingFile ? "" : partnerStoredPath, + image_url: imageUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -947,6 +973,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(partnerBlobUrlRef); + setPartnerPendingFile(null); + if (imageUrl) setPartnerRemoteUrl(imageUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -1101,16 +1130,14 @@ export default function ContentWebsite({ return; } if (contributorMode && editMode) { - if (popupPendingFile) { - await Swal.fire({ - icon: "info", - title: "Gunakan URL gambar", - text: "Sebagai kontributor, gunakan URL banner dari Media Library.", - }); - return; - } setSaving(true); try { + let mediaUrl = (popupRemoteUrl ?? "").trim(); + if (popupPendingFile) { + const up = await uploadContributorAssetToPublicUrl(popupPendingFile); + if (!up) return; + mediaUrl = up; + } const res = await submitCmsContentSubmission({ domain: "popup", title: `Pop-up: ${popupPrimary.slice(0, 80)}`, @@ -1121,7 +1148,7 @@ export default function ContentWebsite({ description: popupDesc, primary_cta: popupCta1, secondary_cta_text: popupCta2, - media_url: (popupRemoteUrl ?? "").trim(), + media_url: mediaUrl, }, }); if ((res as { error?: boolean })?.error) { @@ -1132,6 +1159,9 @@ export default function ContentWebsite({ }); return; } + revokeBlobRef(popupBlobUrlRef); + setPopupPendingFile(null); + if (mediaUrl) setPopupRemoteUrl(mediaUrl); await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", @@ -1325,8 +1355,8 @@ export default function ContentWebsite({ {contributorMode && !editMode && !viewOnly ? (

Aktifkan Edit Mode untuk mengusulkan perubahan. Perubahan akan masuk ke{" "} - My Content menunggu persetujuan approver. Unggah file gambar tidak tersedia sebagai kontributor; - gunakan URL dari Media Library pada bidang yang disediakan. + My Content menunggu persetujuan approver. Gambar dan media dapat diunggah dari perangkat Anda + (disimpan ke Media Library saat Anda menyimpan tab); website live baru berubah setelah disetujui.

) : null} @@ -1405,29 +1435,19 @@ export default function ContentWebsite({

{contributorMode - ? "Tempel URL gambar dari Media Library (kontributor tidak dapat mengunggah file langsung)." + ? "Unggah JPG, PNG, GIF, atau WebP. Saat menyimpan, berkas diunggah ke Media Library dan dilampirkan ke pengajuan." : "Upload a JPG, PNG, GIF, or WebP file. Stored in MinIO and shown on the landing hero."}

- {contributorMode ? ( - setHeroRemoteUrl(e.target.value)} - /> - ) : ( - { - const f = e.target.files?.[0] ?? null; - setPickedFile(f, heroBlobUrlRef, setHeroPendingFile); - e.target.value = ""; - }} - /> - )} + { + const f = e.target.files?.[0] ?? null; + setPickedFile(f, heroBlobUrlRef, setHeroPendingFile); + e.target.value = ""; + }} + /> {heroBlobUrlRef.current || heroRemoteUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -1515,29 +1535,19 @@ export default function ContentWebsite({

{contributorMode - ? "Tempel URL gambar atau video dari Media Library." + ? "Unggah gambar atau video (MP4/WebM). Saat menyimpan, berkas diunggah ke Media Library dan dilampirkan ke pengajuan." : "Upload JPG/PNG/GIF/WebP or MP4/WebM. Stored in MinIO; shown inside the phone mockup on the landing page."}

- {contributorMode ? ( - setAboutRemoteMediaUrl(e.target.value)} - /> - ) : ( - { - const f = e.target.files?.[0] ?? null; - setPickedFile(f, aboutBlobUrlRef, setAboutPendingFile); - e.target.value = ""; - }} - /> - )} + { + const f = e.target.files?.[0] ?? null; + setPickedFile(f, aboutBlobUrlRef, setAboutPendingFile); + e.target.value = ""; + }} + /> {aboutBlobUrlRef.current || aboutRemoteMediaUrl ? (
{aboutPendingFile?.type.startsWith("video/") || @@ -1769,26 +1779,16 @@ export default function ContentWebsite({
- {contributorMode ? ( - setProductRemoteUrl(e.target.value)} - /> - ) : ( - { - const f = e.target.files?.[0] ?? null; - setPickedFile(f, productBlobUrlRef, setProductPendingFile); - e.target.value = ""; - }} - /> - )} + { + const f = e.target.files?.[0] ?? null; + setPickedFile(f, productBlobUrlRef, setProductPendingFile); + e.target.value = ""; + }} + /> {productBlobUrlRef.current || productRemoteUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -1964,26 +1964,16 @@ export default function ContentWebsite({
- {contributorMode ? ( - setServiceRemoteUrl(e.target.value)} - /> - ) : ( - { - const f = e.target.files?.[0] ?? null; - setPickedFile(f, serviceBlobUrlRef, setServicePendingFile); - e.target.value = ""; - }} - /> - )} + { + const f = e.target.files?.[0] ?? null; + setPickedFile(f, serviceBlobUrlRef, setServicePendingFile); + e.target.value = ""; + }} + /> {serviceBlobUrlRef.current || serviceRemoteUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -2129,26 +2119,16 @@ export default function ContentWebsite({
- {contributorMode ? ( - setPartnerRemoteUrl(e.target.value)} - /> - ) : ( - { const f = e.target.files?.[0] ?? null; setPickedFile(f, partnerBlobUrlRef, setPartnerPendingFile); e.target.value = ""; }} /> - )} {partnerBlobUrlRef.current || partnerRemoteUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -2327,26 +2307,16 @@ export default function ContentWebsite({ - {contributorMode ? ( - setPopupRemoteUrl(e.target.value)} - /> - ) : ( - { - const f = e.target.files?.[0] ?? null; - setPickedFile(f, popupBlobUrlRef, setPopupPendingFile); - e.target.value = ""; - }} - /> - )} + { + const f = e.target.files?.[0] ?? null; + setPickedFile(f, popupBlobUrlRef, setPopupPendingFile); + e.target.value = ""; + }} + /> {popupBlobUrlRef.current || popupRemoteUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} diff --git a/service/media-library.ts b/service/media-library.ts index 78fbfc9..03917d5 100644 --- a/service/media-library.ts +++ b/service/media-library.ts @@ -60,6 +60,20 @@ export async function uploadMediaLibraryFile(formData: FormData) { return await httpPostFormDataInterceptor("/media-library/upload", formData); } +/** Reads `public_url` from upload API (`data.data.public_url` after interceptor wrap). */ +export function parseMediaLibraryUploadPublicUrl( + interceptorResult: unknown, +): string | null { + if (!interceptorResult || typeof interceptorResult !== "object") return null; + const r = interceptorResult as { + error?: boolean; + data?: { data?: { public_url?: string } }; + }; + if (r.error) return null; + const url = r.data?.data?.public_url; + return typeof url === "string" && url.trim() ? url.trim() : null; +} + export async function registerMediaLibrary(body: { public_url: string; object_key?: string;