"use client"; import { useEffect, useState } from "react"; import { Upload, Plus, Settings, CheckCheck, Clock, Check, X, } from "lucide-react"; import Image from "next/image"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardContent, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { error, loading, success } from "@/config/swal"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useDropzone } from "react-dropzone"; import z from "zod"; import dynamic from "next/dynamic"; import { useParams, useRouter } from "next/navigation"; import Cookies from "js-cookie"; import { approveProduct, getProductDataById, rejectProduct, updateProduct, } from "@/service/product"; import { convertDateFormat } from "@/utils/global"; import withReactContent from "sweetalert2-react-content"; import Swal from "sweetalert2"; const ViewEditor = dynamic( () => { return import("@/components/editor/view-editor"); }, { ssr: false }, ); const CustomEditor = dynamic( () => { return import("@/components/editor/custom-editor"); }, { ssr: false }, ); interface FileWithPreview extends File { preview: string; } interface CategoryType { id: number; label: string; value: number; } const categorySchema = z.object({ id: z.number(), label: z.string(), value: z.number(), }); const createArticleSchema = z.object({ title: z.string().min(2, { message: "Judul harus diisi", }), variant: z.string().min(2, { message: "variant diisi", }), price: z.string().min(2, { message: "Price harus diisi", }), category: z.array(categorySchema).nonempty({ message: "Kategori harus memiliki setidaknya satu item", }), tags: z.array(z.string()).nonempty({ message: "Minimal 1 tag", }), // Array berisi string }); export default function DetailProductForm(props: { isDetail: boolean }) { const { isDetail } = props; const params = useParams(); const id = params?.id; const username = Cookies.get("username"); const userId = Cookies.get("uie"); const [files, setFiles] = useState([]); const [useAi, setUseAI] = useState(false); const [listCategory, setListCategory] = useState([]); const [tag, setTag] = useState(""); const [detailfiles, setDetailFiles] = useState([]); const [mainImage, setMainImage] = useState(0); const [thumbnail, setThumbnail] = useState(""); const [diseId, setDiseId] = useState(0); const [thumbnailImg, setThumbnailImg] = useState([]); const [selectedMainImage, setSelectedMainImage] = useState( null, ); const [thumbnailValidation, setThumbnailValidation] = useState(""); // const { isOpen, onOpen, onOpenChange } = useDisclosure(); const [isOpen, setIsOpen] = useState(false); const onOpen = () => setIsOpen(true); const onOpenChange = () => setIsOpen((prev) => !prev); const [approvalStatus, setApprovalStatus] = useState(2); const [approvalMessage, setApprovalMessage] = useState(""); const [detailData, setDetailData] = useState(); const [startDateValue, setStartDateValue] = useState(null); const [timeValue, setTimeValue] = useState("00:00"); const [openApproverHistory, setOpenApproverHistory] = useState(false); const [userLevelId, setUserLevelId] = useState(null); const router = useRouter(); // 🔹 Ambil userlevelId dari cookies useEffect(() => { const ulne = Cookies.get("ulne"); // contoh: "3" setUserLevelId(ulne ?? null); }, []); const { getRootProps, getInputProps } = useDropzone({ onDrop: (acceptedFiles) => { setFiles((prevFiles) => [ ...prevFiles, ...acceptedFiles.map((file) => Object.assign(file)), ]); }, multiple: true, accept: { "image/*": [], }, }); const formOptions = { resolver: zodResolver(createArticleSchema), defaultValues: { title: "", description: "", category: [], tags: [] }, }; type UserSettingSchema = z.infer; const { register, control, handleSubmit, formState: { errors }, setValue, getValues, watch, setError, clearErrors, } = useForm(formOptions); const [specs, setSpecs] = useState([ { id: 1, title: "Jaecoo 7 SHS Teknologi dan Exterior", images: ["/spec1.jpg", "/spec2.jpg", "/spec3.jpg", "/spec4.jpg"], }, ]); type ColorType = { id: number; name: string; preview: string; colorSelected: string | null; }; const [colors, setColors] = useState([ { id: 1, name: "", preview: "/car-1.png", colorSelected: null, }, { id: 2, name: "", preview: "/car-2.png", colorSelected: null, }, ]); const palette = [ "#1E4E52", "#597E8D", "#6B6B6B", "#BEBEBE", "#E2E2E2", "#F4F4F4", "#FFFFFF", "#F9360A", "#9A2A00", "#7A1400", "#4B0200", "#B48B84", "#FFA598", ]; const handleAddSpec = () => { setSpecs((prev) => [ ...prev, { id: prev.length + 1, title: "", images: [], }, ]); }; const handleAddColor = () => { setColors((p) => [ ...p, { id: p.length + 1, name: "", preview: "/car-default.png", colorSelected: null, }, ]); }; const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); const [openCommentModal, setOpenCommentModal] = useState(false); const [commentValue, setCommentValue] = useState(""); const handleSubmitComment = async () => { // await api.post("/banner/comment", { // bannerId: viewBanner.id, // comment: commentValue, // }); setOpenCommentModal(false); }; const [uploadTarget, setUploadTarget] = useState<{ type: "spec" | "color"; index: number; } | null>(null); const fileInputId = "file-upload-input"; const handleFileSelected = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file || !uploadTarget) return; const reader = new FileReader(); reader.onload = () => { const fileUrl = reader.result as string; if (uploadTarget.type === "spec") { setSpecs((prev) => { const updated = [...prev]; updated[uploadTarget.index].images.push(fileUrl); return updated; }); } if (uploadTarget.type === "color") { setColors((prev) => { const updated = [...prev]; updated[uploadTarget.index].preview = fileUrl; return updated; }); } }; reader.readAsDataURL(file); setIsUploadDialogOpen(false); }; useEffect(() => { initState(); }, [listCategory]); async function initState() { const res = await getProductDataById(id); const data = res?.data?.data; if (!data) return; setDetailData(data); // form setValue("title", data.title); setValue("variant", data.variant); setValue("price", formatRupiah(data.price)); // thumbnail setThumbnail(data.thumbnail_url); // colors if (data.colors?.length) { setColors( data.colors.map((color: string, index: number) => ({ id: index + 1, name: color, preview: data.thumbnail_url, colorSelected: color, })), ); } } const handleRejectProduct = async (id: number) => { const MySwal = withReactContent(Swal); const { value: message } = await MySwal.fire({ title: "Tolak Product", input: "textarea", inputLabel: "Alasan penolakan (opsional)", inputPlaceholder: "Masukkan alasan penolakan...", inputAttributes: { "aria-label": "Masukkan alasan penolakan", }, showCancelButton: true, confirmButtonText: "Tolak", cancelButtonText: "Batal", confirmButtonColor: "#dc2626", }); if (message === undefined) { return; // User cancelled } loading(); const res = await rejectProduct(id, message || undefined); if (res?.error) { error(res.message || "Gagal menolak product"); close(); return; } close(); success("Product berhasil ditolak"); initState(); // refresh table }; const handleApproveProduct = async (id: number) => { loading(); const res = await approveProduct(id); if (res?.error) { error(res.message || "Gagal menyetujui product"); close(); return; } close(); success("Product berhasil disetujui"); initState(); // refresh table }; const handleOpenApproverHistory = () => { setOpenApproverHistory(true); }; const formatRupiah = (value: string) => "Rp " + Number(value).toLocaleString("id-ID"); return ( <> Detail Produk
(
)} />
(
)} />
(
)} />
{thumbnail ? ( Banner ) : (
No Image
)}
{colors.map((item, index) => (
{palette.map((colorCode) => (
car color
))} {/* */}
{specs.map((spec, index) => (
{spec.images.map((img, i) => ( spec ))} {/* */}
))} {/* */}

Status Timeline

Diupload oleh Operator

{convertDateFormat(detailData?.created_at)} WIB

{detailData?.status_id === 1 ? ( ) : detailData?.status_id === 2 ? ( ) : ( )}

{detailData?.status_id === 1 ? "Menunggu disetujui oleh Approver" : detailData?.status_id === 2 ? "Disetujui oleh Approver" : "Ditolak oleh Approver"}

{convertDateFormat(detailData?.updated_at)} WIB

Comment :

Jaecoo - Approver | 10/11/2026

{userLevelId !== "2" && detailData && (
{detailData.status_id === 1 ? ( <> {userLevelId === "1" && ( )} ) : ( )}
)} {/* */}
{openApproverHistory && (
setOpenApproverHistory(false)} >
e.stopPropagation()} > {/* HEADER */}

Approver History

Menunggu Banner 1
{/* BODY */}
{/* LEFT TIMELINE */}
{/* Upload */}
Upload
{/* Diterima */}

Diterima

Direview oleh: approver-jaecoo1
{/* Pending */}

Pending

Direview oleh: approver-jaecoo1
{/* ARROW */}
> >
{/* RIGHT NOTES */}
Catatan:
Catatan:
{/* FOOTER */}
)} Upload File
document.getElementById(fileInputId)?.click()} >

Klik untuk upload atau drag & drop

PNG, JPG (max 2 MB)

{openCommentModal && detailData && (
setOpenCommentModal(false)} >
e.stopPropagation()} > {/* HEADER */}

Beri Tanggapan

{/* Badge */}
Menunggu Banner {detailData.position}
{/* BODY */}