diff --git a/src/app/admin/review/[productId]/page.tsx b/src/app/admin/review/[productId]/page.tsx index 3a4469d..cb700d3 100644 --- a/src/app/admin/review/[productId]/page.tsx +++ b/src/app/admin/review/[productId]/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useParams, useRouter, useSearchParams } from "next/navigation"; -import { Suspense, useEffect, useState } from "react"; +import { Suspense, useEffect, useRef, useState } from "react"; const API_BASE = process.env.NEXT_PUBLIC_API_URL || "https://be.inatrading.co.id"; @@ -61,6 +61,26 @@ function extractIdChange(payload: unknown) { | undefined; } +function isProductUpdateFromCompare( + change: + | { + oldValue?: string; + newValue?: string; + isUpdate?: boolean; + } + | undefined +) { + if (!change) return false; + const hasDifferentIds = + typeof change.oldValue === "string" && + typeof change.newValue === "string" && + change.oldValue !== "" && + change.newValue !== "" && + change.oldValue !== change.newValue; + + return change.isUpdate === true || hasDifferentIds; +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any function ProductColumn({ product, label, accent }: { product: any; label: string; accent?: boolean }) { if (!product) return ( @@ -208,6 +228,7 @@ function AdminReviewDetailPageInner() { // eslint-disable-next-line @typescript-eslint/no-explicit-any const [oldProduct, setOldProduct] = useState(null); // original (compare) const [isComparison, setIsComparison] = useState(false); + const [isUpdateProduct, setIsUpdateProduct] = useState(false); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(""); @@ -216,6 +237,7 @@ function AdminReviewDetailPageInner() { const [actionSuccess, setActionSuccess] = useState(""); const [showRejectModal, setShowRejectModal] = useState(false); const [rejectReason, setRejectReason] = useState(""); + const redirectTimeoutRef = useRef | null>(null); useEffect(() => { if (!params.productId) return; @@ -223,6 +245,7 @@ function AdminReviewDetailPageInner() { setLoadError(""); setOldProduct(null); setIsComparison(false); + setIsUpdateProduct(false); const reviewFetch = fetch(`/api/admin/review/${params.productId}`, { headers: { "x-auth-token": getToken() }, @@ -254,7 +277,10 @@ function AdminReviewDetailPageInner() { setProduct(updated); - const shouldCompare = idChange?.isUpdate === true && Boolean(idChange.oldValue) && Boolean(idChange.newValue); + const isUpdate = isProductUpdateFromCompare(idChange); + setIsUpdateProduct(isUpdate); + + const shouldCompare = isUpdate && Boolean(idChange?.oldValue) && Boolean(idChange?.newValue); setIsComparison(shouldCompare); if (shouldCompare) { @@ -272,6 +298,14 @@ function AdminReviewDetailPageInner() { .finally(() => setLoading(false)); }, [params.productId]); + useEffect(() => { + return () => { + if (redirectTimeoutRef.current) { + clearTimeout(redirectTimeoutRef.current); + } + }; + }, []); + async function submitReview(action: "accept" | "reject", reason?: string) { setActing(true); setActionError(""); @@ -279,7 +313,7 @@ function AdminReviewDetailPageInner() { const res = await fetch(`/api/admin/review/${params.productId}`, { method: "POST", headers: { "Content-Type": "application/json", "x-auth-token": getToken() }, - body: JSON.stringify({ action, isNew: product?.isNew ?? true, reason }), + body: JSON.stringify({ action, isUpdate: isUpdateProduct, reason }), }); const data = await res.json(); if (!res.ok) { @@ -287,7 +321,12 @@ function AdminReviewDetailPageInner() { return; } setActionSuccess(action === "accept" ? "Update produk berhasil disetujui!" : "Update produk berhasil ditolak!"); - setTimeout(() => router.push("/admin/review"), 1800); + if (redirectTimeoutRef.current) { + clearTimeout(redirectTimeoutRef.current); + } + redirectTimeoutRef.current = setTimeout(() => { + router.push("/admin/review"); + }, 1800); } catch { setActionError("Gagal terhubung ke server"); } finally { diff --git a/src/app/api/admin/review/[productId]/route.ts b/src/app/api/admin/review/[productId]/route.ts index 1bd0306..9edc464 100644 --- a/src/app/api/admin/review/[productId]/route.ts +++ b/src/app/api/admin/review/[productId]/route.ts @@ -17,7 +17,7 @@ export async function GET( } // action: "accept" | "reject" -// isNew: boolean — determines which backend endpoint to call +// isUpdate: boolean — true for product update review, false for new product review // body for reject: { reason: string } export async function POST( req: NextRequest, @@ -25,23 +25,23 @@ export async function POST( ) { const token = req.headers.get("x-auth-token") || ""; const { productId } = await context.params; - const { action, isNew, reason } = await req.json(); + const { action, isUpdate, reason } = await req.json(); let url: string; let method = "POST"; let body: Record | undefined; if (action === "accept") { - // isNew=true: POST /product/accept/{id} - // isNew=false: PUT /product/accept/{id} + // new product review: POST /product/accept/{id} + // product update review: PUT /product/accept/{id} url = `${API_URL}/api/v1.0/product/accept/${productId}`; - method = isNew ? "POST" : "PUT"; + method = isUpdate ? "PUT" : "POST"; body = { state: "PUBLISHED" }; } else { - // isNew=true: POST /product/reject/{id} - // isNew=false: PUT /product/reject/{id} + // new product review: POST /product/reject/{id} + // product update review: PUT /product/reject/{id} url = `${API_URL}/api/v1.0/product/reject/${productId}`; - method = isNew ? "POST" : "PUT"; + method = isUpdate ? "PUT" : "POST"; body = reason ? { reason, state: "REJECTED" } : { state: "REJECTED" }; }