"use client"; import { useCallback, useEffect, useRef, useState, type MutableRefObject, } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Eye, Pencil, ImageIcon, Loader2, Plus, Trash2 } from "lucide-react"; import Swal from "sweetalert2"; import type { CmsAboutContent, CmsHeroContent, CmsPartnerContent, CmsPopupContent, CmsProductContent, CmsServiceContent, } from "@/types/cms-landing"; import { apiDataList, apiPayload, apiRows, deleteAboutUsContentImage, deleteOurProductContent, deleteOurServiceContent, deletePartnerContent, deletePopupNews, getAboutContentsList, getHeroContent, getOurProductContent, getOurServiceContent, getPartnerContents, getPopupNewsList, saveAboutContent, saveAboutUsMediaUpload, saveHeroContent, saveHeroImageUpload, saveOurProductContent, saveOurServiceContent, savePartnerContent, saveOurProductImageUpload, saveOurServiceImageUpload, savePopupNews, savePopupNewsImageUpload, updateAboutContent, updateHeroContent, updateHeroImageUpload, updateOurProductContent, updateOurProductImageUpload, updateOurServiceContent, updateOurServiceImageUpload, updatePartnerContent, updatePopupNews, uploadPartnerLogo, } from "@/service/cms-landing"; import { submitCmsContentSubmission } from "@/service/cms-content-submissions"; function revokeBlobRef(ref: MutableRefObject) { if (ref.current) { URL.revokeObjectURL(ref.current); ref.current = null; } } function setPickedFile( file: File | null, ref: MutableRefObject, setter: (f: File | null) => void, ) { revokeBlobRef(ref); if (file) { ref.current = URL.createObjectURL(file); } setter(file); } type ContentWebsiteProps = { /** User level 2: changes go through approval instead of live CMS APIs. */ contributorMode?: boolean; /** Approver (or admin): load live CMS data but disable all edits (all tabs). */ viewOnly?: boolean; /** Omit page title/actions row — parent supplies section headings (e.g. approver layout). */ hideHeader?: boolean; /** Parent increments this (e.g. 1,2,3…) to switch the visible tab. */ tabFocusSignal?: number; /** Tab id matching `TabsTrigger` values: hero | about | products | services | partners | popup */ tabFocusTarget?: string; /** Increment (e.g. after approver applies CMS) to reload live data from API without remounting. */ liveDataReloadSignal?: number; }; export default function ContentWebsite({ contributorMode = false, viewOnly = false, hideHeader = false, tabFocusSignal = 0, tabFocusTarget = "", liveDataReloadSignal = 0, }: ContentWebsiteProps) { const [activeTab, setActiveTab] = useState("hero"); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [editMode, setEditMode] = useState(!contributorMode); const canInteract = (!contributorMode || editMode) && !viewOnly; const dimContributorPreview = contributorMode && !editMode && !viewOnly; const [heroId, setHeroId] = useState(null); const [heroImageId, setHeroImageId] = useState(null); const [heroPrimary, setHeroPrimary] = useState(""); const [heroSecondary, setHeroSecondary] = useState(""); const [heroDesc, setHeroDesc] = useState(""); const [heroCta1, setHeroCta1] = useState(""); const [heroCta2, setHeroCta2] = useState(""); const [heroRemoteUrl, setHeroRemoteUrl] = useState(""); const [heroPendingFile, setHeroPendingFile] = useState(null); const heroBlobUrlRef = useRef(null); const [aboutId, setAboutId] = useState(null); const [aboutPrimary, setAboutPrimary] = useState(""); const [aboutSecondary, setAboutSecondary] = useState(""); const [aboutDesc, setAboutDesc] = useState(""); const [aboutCta1, setAboutCta1] = useState(""); const [aboutCta2, setAboutCta2] = useState(""); const [aboutRemoteMediaUrl, setAboutRemoteMediaUrl] = useState(""); const [aboutPendingFile, setAboutPendingFile] = useState(null); const aboutBlobUrlRef = useRef(null); const [aboutMediaImageId, setAboutMediaImageId] = useState( null, ); const [products, setProducts] = useState([]); const [productEditId, setProductEditId] = useState(null); const [productPrimary, setProductPrimary] = useState(""); const [productSecondary, setProductSecondary] = useState(""); const [productDesc, setProductDesc] = useState(""); const [productLinkUrl, setProductLinkUrl] = useState(""); const [productRemoteUrl, setProductRemoteUrl] = useState(""); const [productPendingFile, setProductPendingFile] = useState( null, ); const productBlobUrlRef = useRef(null); const [productImageId, setProductImageId] = useState(null); const [productModalOpen, setProductModalOpen] = useState(false); const [services, setServices] = useState([]); const [serviceEditId, setServiceEditId] = useState(null); const [servicePrimary, setServicePrimary] = useState(""); const [serviceSecondary, setServiceSecondary] = useState(""); const [serviceDesc, setServiceDesc] = useState(""); const [serviceLinkUrl, setServiceLinkUrl] = useState(""); const [serviceRemoteUrl, setServiceRemoteUrl] = useState(""); const [servicePendingFile, setServicePendingFile] = useState( null, ); const serviceBlobUrlRef = useRef(null); const [serviceImageId, setServiceImageId] = useState(null); const [serviceModalOpen, setServiceModalOpen] = useState(false); const [partners, setPartners] = useState([]); const [partnerTitle, setPartnerTitle] = useState(""); const [partnerRemoteUrl, setPartnerRemoteUrl] = useState(""); const [partnerStoredPath, setPartnerStoredPath] = useState(""); const [partnerPendingFile, setPartnerPendingFile] = useState( null, ); const partnerBlobUrlRef = useRef(null); const [editingPartnerId, setEditingPartnerId] = useState(null); const [partnerModalOpen, setPartnerModalOpen] = useState(false); const [popups, setPopups] = useState([]); const [popupEditId, setPopupEditId] = useState(null); const [popupPrimary, setPopupPrimary] = useState(""); const [popupSecondary, setPopupSecondary] = useState(""); const [popupDesc, setPopupDesc] = useState(""); const [popupCta1, setPopupCta1] = useState(""); const [popupCta2, setPopupCta2] = useState(""); const [popupRemoteUrl, setPopupRemoteUrl] = useState(""); const [popupPendingFile, setPopupPendingFile] = useState(null); const popupBlobUrlRef = useRef(null); const [popupModalOpen, setPopupModalOpen] = useState(false); const loadAll = useCallback(async () => { setLoading(true); try { const heroRes = await getHeroContent(); const hero = apiPayload(heroRes) as CmsHeroContent | null; if (hero?.id) { setHeroId(hero.id); setHeroPrimary(hero.primary_title ?? ""); setHeroSecondary(hero.secondary_title ?? ""); setHeroDesc(hero.description ?? ""); setHeroCta1(hero.primary_cta ?? ""); setHeroCta2(hero.secondary_cta_text ?? ""); const first = hero.images?.[0]; revokeBlobRef(heroBlobUrlRef); setHeroPendingFile(null); if (first?.image_url) { setHeroRemoteUrl(first.image_url); setHeroImageId(first.id ?? null); } else { setHeroRemoteUrl(""); setHeroImageId(null); } } else { setHeroId(null); setHeroImageId(null); setHeroPrimary(""); setHeroSecondary(""); setHeroDesc(""); setHeroCta1(""); setHeroCta2(""); revokeBlobRef(heroBlobUrlRef); setHeroPendingFile(null); setHeroRemoteUrl(""); } const aboutRes = await getAboutContentsList(); const aboutList = apiRows(aboutRes) as CmsAboutContent[]; const ab = aboutList[0]; if (ab) { setAboutId(ab.id); setAboutPrimary(ab.primary_title ?? ""); setAboutSecondary(ab.secondary_title ?? ""); setAboutDesc(ab.description ?? ""); setAboutCta1(ab.primary_cta ?? ""); setAboutCta2(ab.secondary_cta_text ?? ""); const am = ab.images?.[0]; const murl = am?.media_url?.trim() ?? ""; revokeBlobRef(aboutBlobUrlRef); setAboutPendingFile(null); setAboutRemoteMediaUrl(murl); setAboutMediaImageId(am?.id ?? null); } else { setAboutId(null); setAboutPrimary(""); setAboutSecondary(""); setAboutDesc(""); setAboutCta1(""); setAboutCta2(""); revokeBlobRef(aboutBlobUrlRef); setAboutPendingFile(null); setAboutRemoteMediaUrl(""); setAboutMediaImageId(null); } const prRes = await getOurProductContent(); setProducts(apiDataList(prRes)); const svRes = await getOurServiceContent(); setServices(apiDataList(svRes)); const parRes = await getPartnerContents(); const parList = apiPayload(parRes) as CmsPartnerContent[] | null; setPartners(Array.isArray(parList) ? parList : []); const popRes = await getPopupNewsList(1, 50); setPopups(apiRows(popRes) as CmsPopupContent[]); } finally { setLoading(false); } }, []); useEffect(() => { loadAll(); }, [loadAll]); useEffect(() => { if (!viewOnly) return; setProductModalOpen(false); setServiceModalOpen(false); setPartnerModalOpen(false); setPopupModalOpen(false); }, [viewOnly]); useEffect(() => { if (tabFocusSignal < 1 || !tabFocusTarget) return; setActiveTab(tabFocusTarget); }, [tabFocusSignal, tabFocusTarget]); useEffect(() => { if (liveDataReloadSignal < 1) return; void loadAll(); }, [liveDataReloadSignal, loadAll]); async function saveHeroTab() { if (!heroPrimary.trim()) { await Swal.fire({ icon: "warning", title: "Main title is required" }); 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 { const res = await submitCmsContentSubmission({ domain: "hero", title: `Hero: ${heroPrimary.slice(0, 80)}`, payload: { hero_id: heroId ?? "", hero_image_id: heroImageId ?? "", primary_title: heroPrimary, secondary_title: heroSecondary, description: heroDesc, primary_cta: heroCta1, secondary_cta_text: heroCta2, image_url: (heroRemoteUrl ?? "").trim(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", text: "Perubahan Hero ada di My Content.", timer: 2000, showConfirmButton: false, }); } finally { setSaving(false); } return; } setSaving(true); try { const body = { primary_title: heroPrimary, secondary_title: heroSecondary, description: heroDesc, primary_cta: heroCta1, secondary_cta_text: heroCta2, }; let hid = heroId; if (!hid) { const res = await saveHeroContent(body); const created = apiPayload(res) as CmsHeroContent | null; if (!created?.id) { await Swal.fire({ icon: "error", title: "Failed to create hero content" }); return; } hid = created.id; setHeroId(hid); } else { const res = await updateHeroContent(hid, body); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } if (heroPendingFile && hid) { const fd = new FormData(); fd.append("file", heroPendingFile); let imgRes; if (heroImageId) { imgRes = await updateHeroImageUpload(heroImageId, fd); } else { fd.append("hero_content_id", hid); imgRes = await saveHeroImageUpload(fd); } if (imgRes?.error) { await Swal.fire({ icon: "error", title: "Hero image upload failed", text: String(imgRes.message ?? ""), }); return; } revokeBlobRef(heroBlobUrlRef); setHeroPendingFile(null); } await Swal.fire({ icon: "success", title: "Hero section saved", timer: 1600, showConfirmButton: false }); await loadAll(); } finally { setSaving(false); } } async function saveAboutTab() { if (!aboutPrimary.trim()) { await Swal.fire({ icon: "warning", title: "Main title is required" }); 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 { const res = await submitCmsContentSubmission({ domain: "about", title: `About: ${aboutPrimary.slice(0, 80)}`, payload: { about_id: aboutId ?? undefined, about_media_image_id: aboutMediaImageId ?? undefined, primary_title: aboutPrimary, secondary_title: aboutSecondary, description: aboutDesc, primary_cta: aboutCta1, secondary_cta_text: aboutCta2, media_url: (aboutRemoteMediaUrl ?? "").trim(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", timer: 2000, showConfirmButton: false, }); } finally { setSaving(false); } return; } setSaving(true); try { const body: Record = { primary_title: aboutPrimary, secondary_title: aboutSecondary, description: aboutDesc, primary_cta: aboutCta1, secondary_cta_text: aboutCta2, }; let aid = aboutId; if (aboutId == null) { const res = await saveAboutContent(body); if (res?.error) { await Swal.fire({ icon: "error", title: "Save failed", text: String(res.message ?? "") }); return; } const created = apiPayload(res) as CmsAboutContent | null; aid = created?.id ?? null; if (aid != null) setAboutId(aid); } else { const res = await updateAboutContent(aboutId, body); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } if (aid != null && aboutPendingFile) { if (aboutMediaImageId != null) { await deleteAboutUsContentImage(aboutMediaImageId); } const fd = new FormData(); fd.append("about_us_content_id", String(aid)); fd.append("file", aboutPendingFile); const mres = await saveAboutUsMediaUpload(fd); if (mres?.error) { await Swal.fire({ icon: "error", title: "Media upload failed", text: String(mres.message ?? ""), }); return; } revokeBlobRef(aboutBlobUrlRef); setAboutPendingFile(null); } await Swal.fire({ icon: "success", title: "About Us saved", timer: 1600, showConfirmButton: false }); await loadAll(); } finally { setSaving(false); } } function beginEditProduct(p: CmsProductContent | null) { if (!p) { setProductEditId(null); setProductPrimary(""); setProductSecondary(""); setProductDesc(""); setProductLinkUrl(""); revokeBlobRef(productBlobUrlRef); setProductPendingFile(null); setProductRemoteUrl(""); setProductImageId(null); return; } setProductEditId(p.id); setProductPrimary(p.primary_title ?? ""); setProductSecondary(p.secondary_title ?? ""); setProductDesc(p.description ?? ""); setProductLinkUrl(p.link_url ?? ""); const im = p.images?.[0]; revokeBlobRef(productBlobUrlRef); setProductPendingFile(null); setProductRemoteUrl(im?.image_url?.trim() ?? ""); setProductImageId(im?.id ?? null); } function openProductModalCreate() { beginEditProduct(null); setProductModalOpen(true); } function openProductModalEdit(p: CmsProductContent) { beginEditProduct(p); setProductModalOpen(true); } async function saveProductDraft() { if (!productPrimary.trim()) { await Swal.fire({ icon: "warning", title: "Product title is required" }); 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 { const res = await submitCmsContentSubmission({ domain: "product", title: `Product: ${productPrimary.slice(0, 80)}`, payload: { product_id: productEditId ?? "", product_image_id: productImageId ?? "", primary_title: productPrimary, secondary_title: productSecondary, description: productDesc, link_url: productLinkUrl, image_url: (productRemoteUrl ?? "").trim(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", timer: 1800, showConfirmButton: false, }); beginEditProduct(null); setProductModalOpen(false); } finally { setSaving(false); } return; } setSaving(true); try { const body = { primary_title: productPrimary, secondary_title: productSecondary, description: productDesc, link_url: productLinkUrl, }; let pid = productEditId; if (!pid) { const res = await saveOurProductContent(body); const created = apiPayload(res) as CmsProductContent | null; if (!created?.id) { await Swal.fire({ icon: "error", title: "Failed to create product" }); return; } pid = created.id; } else { const res = await updateOurProductContent(pid, body); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } if (productPendingFile && pid) { const fd = new FormData(); fd.append("file", productPendingFile); let ires; if (productImageId) { ires = await updateOurProductImageUpload(productImageId, fd); } else { fd.append("our_product_content_id", pid); ires = await saveOurProductImageUpload(fd); } if (ires?.error) { await Swal.fire({ icon: "error", title: "Product image upload failed", text: String(ires.message ?? ""), }); return; } revokeBlobRef(productBlobUrlRef); setProductPendingFile(null); } await Swal.fire({ icon: "success", title: "Product saved", timer: 1400, showConfirmButton: false }); await loadAll(); beginEditProduct(null); setProductModalOpen(false); } finally { setSaving(false); } } async function removeProduct(id: string) { const ok = await Swal.fire({ icon: "warning", title: "Delete this product?", showCancelButton: true, }); if (!ok.isConfirmed) return; if (contributorMode && editMode) { setSaving(true); try { const res = await submitCmsContentSubmission({ domain: "product", title: `Delete product ${id}`, payload: { action: "delete", product_id: id }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Penghapusan diajukan", timer: 1600, showConfirmButton: false, }); if (productEditId === id) { beginEditProduct(null); setProductModalOpen(false); } await loadAll(); } finally { setSaving(false); } return; } setSaving(true); try { await deleteOurProductContent(id); if (productEditId === id) { beginEditProduct(null); setProductModalOpen(false); } await loadAll(); } finally { setSaving(false); } } function beginEditService(s: CmsServiceContent | null) { if (!s) { setServiceEditId(null); setServicePrimary(""); setServiceSecondary(""); setServiceDesc(""); setServiceLinkUrl(""); revokeBlobRef(serviceBlobUrlRef); setServicePendingFile(null); setServiceRemoteUrl(""); setServiceImageId(null); return; } setServiceEditId(s.id); setServicePrimary(s.primary_title ?? ""); setServiceSecondary(s.secondary_title ?? ""); setServiceDesc(s.description ?? ""); setServiceLinkUrl(s.link_url ?? ""); const im = s.images?.[0]; revokeBlobRef(serviceBlobUrlRef); setServicePendingFile(null); setServiceRemoteUrl(im?.image_url?.trim() ?? ""); setServiceImageId(im?.id != null ? String(im.id) : null); } function openServiceModalCreate() { beginEditService(null); setServiceModalOpen(true); } function openServiceModalEdit(s: CmsServiceContent) { beginEditService(s); setServiceModalOpen(true); } async function saveServiceDraft() { if (!servicePrimary.trim()) { await Swal.fire({ icon: "warning", title: "Service title is required" }); 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 { const res = await submitCmsContentSubmission({ domain: "service", title: `Service: ${servicePrimary.slice(0, 80)}`, payload: { service_id: serviceEditId ?? undefined, service_image_id: serviceImageId ?? "", primary_title: servicePrimary, secondary_title: serviceSecondary, description: serviceDesc, link_url: serviceLinkUrl, image_url: (serviceRemoteUrl ?? "").trim(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", timer: 1800, showConfirmButton: false, }); beginEditService(null); setServiceModalOpen(false); } finally { setSaving(false); } return; } setSaving(true); try { const body = { primary_title: servicePrimary, secondary_title: serviceSecondary, description: serviceDesc, }; let sid = serviceEditId; if (sid == null) { const res = await saveOurServiceContent(body); const created = apiPayload(res) as CmsServiceContent | null; if (created?.id == null) { await Swal.fire({ icon: "error", title: "Failed to create service" }); return; } sid = created.id; } else { const res = await updateOurServiceContent(sid, body); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } if (servicePendingFile && sid != null) { const fd = new FormData(); fd.append("file", servicePendingFile); let ires; if (serviceImageId) { ires = await updateOurServiceImageUpload(serviceImageId, fd); } else { fd.append("our_service_content_id", String(sid)); ires = await saveOurServiceImageUpload(fd); } if (ires?.error) { await Swal.fire({ icon: "error", title: "Service image upload failed", text: String(ires.message ?? ""), }); return; } revokeBlobRef(serviceBlobUrlRef); setServicePendingFile(null); } await Swal.fire({ icon: "success", title: "Service saved", timer: 1400, showConfirmButton: false }); await loadAll(); beginEditService(null); setServiceModalOpen(false); } finally { setSaving(false); } } async function removeService(id: number) { const ok = await Swal.fire({ icon: "warning", title: "Delete this service?", showCancelButton: true, }); if (!ok.isConfirmed) return; if (contributorMode && editMode) { setSaving(true); try { const res = await submitCmsContentSubmission({ domain: "service", title: `Delete service ${id}`, payload: { action: "delete", service_id: id }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Penghapusan diajukan", timer: 1600, showConfirmButton: false, }); if (serviceEditId === id) { beginEditService(null); setServiceModalOpen(false); } await loadAll(); } finally { setSaving(false); } return; } setSaving(true); try { await deleteOurServiceContent(id); if (serviceEditId === id) { beginEditService(null); setServiceModalOpen(false); } await loadAll(); } finally { setSaving(false); } } function beginEditPartner(p: CmsPartnerContent | null) { if (!p) { setEditingPartnerId(null); setPartnerTitle(""); revokeBlobRef(partnerBlobUrlRef); setPartnerPendingFile(null); setPartnerRemoteUrl(""); setPartnerStoredPath(""); return; } setEditingPartnerId(p.id); setPartnerTitle(p.primary_title ?? ""); revokeBlobRef(partnerBlobUrlRef); setPartnerPendingFile(null); setPartnerRemoteUrl(p.image_url ?? ""); setPartnerStoredPath(p.image_path ?? ""); } function openPartnerModalCreate() { beginEditPartner(null); setPartnerModalOpen(true); } function openPartnerModalEdit(p: CmsPartnerContent) { beginEditPartner(p); setPartnerModalOpen(true); } async function savePartnerRow() { if (!partnerTitle.trim()) { await Swal.fire({ icon: "warning", title: "Partner name is required" }); 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 { 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(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", timer: 1800, showConfirmButton: false, }); beginEditPartner(null); setPartnerModalOpen(false); } finally { setSaving(false); } return; } setSaving(true); try { const body = { primary_title: partnerTitle.trim(), }; let partnerId = editingPartnerId; if (editingPartnerId) { const res = await updatePartnerContent(editingPartnerId, { primary_title: body.primary_title, image_path: partnerStoredPath, image_url: partnerRemoteUrl, }); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } else { const res = await savePartnerContent(body); if (res?.error) { await Swal.fire({ icon: "error", title: "Save failed", text: String(res.message ?? "") }); return; } const created = apiPayload(res) as CmsPartnerContent | null; partnerId = created?.id ?? null; } if (partnerPendingFile && partnerId) { const fd = new FormData(); fd.append("file", partnerPendingFile); const up = await uploadPartnerLogo(partnerId, fd); if (up?.error) { await Swal.fire({ icon: "error", title: "Logo upload failed", text: String(up.message ?? ""), }); return; } revokeBlobRef(partnerBlobUrlRef); setPartnerPendingFile(null); } beginEditPartner(null); setPartnerModalOpen(false); await Swal.fire({ icon: "success", title: "Partner saved", timer: 1400, showConfirmButton: false }); await loadAll(); } finally { setSaving(false); } } async function removePartner(id: string) { const ok = await Swal.fire({ icon: "warning", title: "Delete this partner?", showCancelButton: true, }); if (!ok.isConfirmed) return; if (contributorMode && editMode) { setSaving(true); try { const res = await submitCmsContentSubmission({ domain: "partner", title: `Delete partner ${id}`, payload: { action: "delete", partner_id: id }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Penghapusan diajukan", timer: 1600, showConfirmButton: false, }); if (editingPartnerId === id) { beginEditPartner(null); setPartnerModalOpen(false); } await loadAll(); } finally { setSaving(false); } return; } setSaving(true); try { await deletePartnerContent(id); if (editingPartnerId === id) { beginEditPartner(null); setPartnerModalOpen(false); } await loadAll(); } finally { setSaving(false); } } function beginEditPopup(p: CmsPopupContent | null) { if (!p) { setPopupEditId(null); setPopupPrimary(""); setPopupSecondary(""); setPopupDesc(""); setPopupCta1(""); setPopupCta2(""); revokeBlobRef(popupBlobUrlRef); setPopupPendingFile(null); setPopupRemoteUrl(""); return; } setPopupEditId(p.id); setPopupPrimary(p.primary_title ?? ""); setPopupSecondary(p.secondary_title ?? ""); setPopupDesc(p.description ?? ""); setPopupCta1(p.primary_cta ?? ""); setPopupCta2(p.secondary_cta_text ?? ""); revokeBlobRef(popupBlobUrlRef); setPopupPendingFile(null); setPopupRemoteUrl(p.images?.[0]?.media_url?.trim() ?? ""); } function openPopupModalCreate() { beginEditPopup(null); setPopupModalOpen(true); } function openPopupModalEdit(p: CmsPopupContent) { beginEditPopup(p); setPopupModalOpen(true); } async function savePopupDraft() { if (!popupPrimary.trim()) { await Swal.fire({ icon: "warning", title: "Main title is required" }); 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 { const res = await submitCmsContentSubmission({ domain: "popup", title: `Pop-up: ${popupPrimary.slice(0, 80)}`, payload: { popup_id: popupEditId ?? undefined, primary_title: popupPrimary, secondary_title: popupSecondary, description: popupDesc, primary_cta: popupCta1, secondary_cta_text: popupCta2, media_url: (popupRemoteUrl ?? "").trim(), }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Diajukan untuk persetujuan", timer: 1800, showConfirmButton: false, }); beginEditPopup(null); setPopupModalOpen(false); } finally { setSaving(false); } return; } setSaving(true); try { const body = { primary_title: popupPrimary, secondary_title: popupSecondary, description: popupDesc, primary_cta: popupCta1, secondary_cta_text: popupCta2, }; let pid = popupEditId; if (pid == null) { const res = await savePopupNews(body); if (res?.error) { await Swal.fire({ icon: "error", title: "Save failed", text: String(res.message ?? "") }); return; } const created = apiPayload(res) as CmsPopupContent | null; pid = created?.id ?? null; if (pid == null) { await Swal.fire({ icon: "error", title: "Save failed", text: "Could not read new pop-up id from server.", }); return; } } else { const res = await updatePopupNews(pid, { id: pid, ...body }); if (res?.error) { await Swal.fire({ icon: "error", title: "Update failed", text: String(res.message ?? "") }); return; } } if (popupPendingFile && pid != null) { const fd = new FormData(); fd.append("popup_news_content_id", String(pid)); fd.append("file", popupPendingFile); const imgRes = await savePopupNewsImageUpload(fd); if (imgRes?.error) { await Swal.fire({ icon: "error", title: "Popup image upload failed", text: String(imgRes.message ?? ""), }); return; } revokeBlobRef(popupBlobUrlRef); setPopupPendingFile(null); } await Swal.fire({ icon: "success", title: "Pop up saved", timer: 1600, showConfirmButton: false }); await loadAll(); beginEditPopup(null); setPopupModalOpen(false); } finally { setSaving(false); } } async function removePopup(id: number) { const ok = await Swal.fire({ icon: "warning", title: "Delete this pop up?", showCancelButton: true, }); if (!ok.isConfirmed) return; if (contributorMode && editMode) { setSaving(true); try { const res = await submitCmsContentSubmission({ domain: "popup", title: `Delete popup ${id}`, payload: { action: "delete", popup_id: id }, }); if ((res as { error?: boolean })?.error) { await Swal.fire({ icon: "error", title: "Pengajuan gagal", text: String((res as { message?: unknown })?.message ?? ""), }); return; } await Swal.fire({ icon: "success", title: "Penghapusan diajukan", timer: 1600, showConfirmButton: false, }); if (popupEditId === id) { beginEditPopup(null); setPopupModalOpen(false); } await loadAll(); } finally { setSaving(false); } return; } setSaving(true); try { const res = await deletePopupNews(id); if (res?.error) { await Swal.fire({ icon: "error", title: "Delete failed", text: String(res.message ?? "Could not delete pop up."), }); return; } if (popupEditId === id) { beginEditPopup(null); setPopupModalOpen(false); } await loadAll(); await Swal.fire({ icon: "success", title: "Deleted", timer: 1200, showConfirmButton: false }); } finally { setSaving(false); } } if (loading) { return (
); } return (
{!hideHeader ? (

Content Website

{viewOnly ? "Konten live di semua tab: hanya lihat. Pengajuan perubahan ditangani di bagian atas halaman." : "Update homepage content, products, services, and partners."}

{contributorMode ? ( ) : null}
) : viewOnly ? (
) : null} {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.

) : null}
Hero Section About Us Our Products Our Services Technology Partners Pop Up
setHeroPrimary(e.target.value)} placeholder="Headline" />
setHeroSecondary(e.target.value)} placeholder="" />