update table agent,banner,promotion,galery,product and approve flow

This commit is contained in:
Anang Yusman 2026-01-20 14:47:23 +08:00
parent 5579ad8c20
commit c2c08a1c4e
9 changed files with 910 additions and 173 deletions

View File

@ -8,14 +8,39 @@ import {
DialogFooter, DialogFooter,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import Image from "next/image"; import Image from "next/image";
import { CheckCircle } from "lucide-react"; import { Check, CheckCheck, CheckCircle, Clock, X } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { getGaleryFileData } from "@/service/galery"; import { approveGalery, getGaleryFileData } from "@/service/galery";
import { convertDateFormat } from "@/utils/global";
import { Button } from "../ui/button";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { error, loading, success } from "@/config/swal";
export function DialogDetailGaleri({ open, onClose, data }: any) { export function DialogDetailGaleri({ open, onClose, data }: any) {
const [images, setImages] = useState<any[]>([]); const [images, setImages] = useState<any[]>([]);
const [openApproverHistory, setOpenApproverHistory] = useState(false); const [openApproverHistory, setOpenApproverHistory] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [openViewDialog, setOpenViewDialog] = useState(false);
const router = useRouter();
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
}, []);
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 fetchImages = async () => { const fetchImages = async () => {
try { try {
const res = await getGaleryFileData(data.id); const res = await getGaleryFileData(data.id);
@ -44,6 +69,21 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
setOpenApproverHistory(true); setOpenApproverHistory(true);
}; };
const handleApproveGalery = async (id: number) => {
loading();
const res = await approveGalery(id);
if (res?.error) {
error(res.message || "Gagal menyetujui galeri");
close();
return;
}
close();
success("Galeri berhasil disetujui");
fetchImages(); // refresh table
};
return ( return (
<> <>
<Dialog open={open} onOpenChange={onClose}> <Dialog open={open} onOpenChange={onClose}>
@ -107,43 +147,59 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
{/* Timeline */} {/* Timeline */}
<div> <div>
<p className="font-medium text-sm text-gray-700 mb-2"> <h4 className="text-sm font-semibold text-gray-700 mb-3">
Status Timeline Status Timeline
</p> </h4>
<div className="flex flex-col gap-3"> <div className="space-y-4">
<div className="flex items-start gap-3"> <div className="flex gap-3">
<CheckCircle className="text-green-600" /> <div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
<Check className="w-4 h-4 text-green-600" />
</div>
<div> <div>
<p className="font-semibold">Diupload Oleh :</p> <p className="font-medium text-gray-800">
<p className="text-gray-500 text-sm"> Diupload oleh Operator
{new Date(data.created_at).toLocaleString("id-ID")}
</p> </p>
</div> <p className="text-sm text-gray-500">
</div> {convertDateFormat(data?.created_at)} WIB
<div className="flex items-start gap-3">
<CheckCircle className="text-green-600" />
<div>
<p className="font-semibold">Disetujui Oleh :</p>
<p className="text-gray-500 text-sm">
{new Date(data.created_at).toLocaleString("id-ID")}
</p> </p>
</div> </div>
</div> </div>
{data.approved_at && ( <div className="flex gap-3">
<div className="flex items-start gap-3"> <div
<CheckCircle className="text-green-600" /> className={`w-6 h-6 rounded-full flex items-center justify-center ${
<div> data?.status_id === 1
<p className="font-semibold">Disetujui oleh Approver</p> ? "bg-yellow-100"
<p className="text-gray-500 text-sm"> : data?.status_id === 2
{new Date(data.approved_at).toLocaleString("id-ID")} ? "bg-green-100"
</p> : "bg-red-100"
</div> }`}
</div> >
{data?.status_id === 1 ? (
<Clock className="w-4 h-4 text-yellow-700" />
) : data?.status_id === 2 ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-red-700" />
)} )}
</div> </div>
<div>
<p className="font-medium text-gray-800">
{data?.status_id === 1
? "Menunggu disetujui oleh Approver"
: data?.status_id === 2
? "Disetujui oleh Approver"
: "Ditolak oleh Approver"}
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(data?.updated_at)} WIB
</p>
</div> </div>
</div>
<div className="border rounded-lg px-3 py-3"> <div className="border rounded-lg px-3 py-3">
<p>Comment : </p> <p>Comment : </p>
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
@ -157,6 +213,63 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
</div> </div>
</div> </div>
</div> </div>
</div>
{openViewDialog && userLevelId !== "2" && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{data.status_id === 1 ? (
<>
<Button
variant="secondary"
className="bg-blue-200 hover:bg-blue-400"
onClick={(e) => {
e.stopPropagation();
setOpenCommentModal(true);
}}
>
Beri Tanggapan
</Button>
<Button
className=" w-[180]"
variant="destructive"
// onClick={(e) => {
// e.stopPropagation();
// handleReject();
// }}
>
Reject
</Button>
{userLevelId === "1" && (
<Button
// variant="ghost"
size="sm"
className="bg-green-600 hover:bg-green-700 text-white w-[180]"
onClick={(e) => {
e.stopPropagation();
handleApproveGalery(data.id);
}}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)}
</>
) : (
<Button
variant="secondary"
className="mx-auto"
onClick={(e) => {
e.stopPropagation();
setOpenViewDialog(false);
}}
>
Tutup
</Button>
)}
</div>
)}
</div>
{/* <DialogFooter className="px-6 pb-6"> {/* <DialogFooter className="px-6 pb-6">
<button <button

View File

@ -7,8 +7,20 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { CheckCircle2, FileText } from "lucide-react"; import {
import { getPromotionById } from "@/service/promotion"; Check,
CheckCheck,
CheckCircle2,
Clock,
FileText,
X,
} from "lucide-react";
import { approvePromotion, getPromotionById } from "@/service/promotion";
import { convertDateFormat } from "@/utils/global";
import { Button } from "../ui/button";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { error, loading, success } from "@/config/swal";
type PromoDetailDialogProps = { type PromoDetailDialogProps = {
promoId: number | null; promoId: number | null;
@ -21,10 +33,20 @@ export default function PromoDetailDialog({
open, open,
onOpenChange, onOpenChange,
}: PromoDetailDialogProps) { }: PromoDetailDialogProps) {
const [loading, setLoading] = useState(false); const [loadingData, setLoadingData] = useState(false);
const [promo, setPromo] = useState<any>(null); const [promo, setPromo] = useState<any>(null);
const [openApproverHistory, setOpenApproverHistory] = useState(false); const [openApproverHistory, setOpenApproverHistory] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const router = useRouter();
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
}, []);
// FORMAT TANGGAL → DD-MM-YYYY // FORMAT TANGGAL → DD-MM-YYYY
const formatDate = (dateStr: string) => { const formatDate = (dateStr: string) => {
const d = new Date(dateStr); const d = new Date(dateStr);
@ -37,17 +59,37 @@ export default function PromoDetailDialog({
const handleOpenApproverHistory = () => { const handleOpenApproverHistory = () => {
setOpenApproverHistory(true); setOpenApproverHistory(true);
}; };
const handleApprovePromotion = async (promoId: number) => {
loading();
const res = await approvePromotion(promoId);
if (res?.error) {
error(res.message || "Gagal menyetujui promotion");
close();
return;
}
close();
success("Promotion berhasil disetujui");
// fetchData(); // refresh table
};
useEffect(() => { useEffect(() => {
if (!promoId || !open) return; if (!promoId || !open) return;
async function fetchData() { async function fetchData() {
try { try {
setLoading(true); setLoadingData(true);
const res = await getPromotionById(promoId); const res = await getPromotionById(promoId);
// Mapping ke format dialog // Mapping ke format dialog
const mapped = { const mapped = {
id: res?.data?.data?.id,
title: res?.data?.data?.title, title: res?.data?.data?.title,
status_id: res?.data?.data?.status_id,
created_at: res?.data?.data?.created_at,
updated_at: res?.data?.data?.updated_at,
fileSize: "Tidak diketahui", fileSize: "Tidak diketahui",
uploadDate: formatDate(res?.data?.data?.created_at), uploadDate: formatDate(res?.data?.data?.created_at),
status: res?.data?.data?.is_active ? "Aktif" : "Nonaktif", status: res?.data?.data?.is_active ? "Aktif" : "Nonaktif",
@ -75,19 +117,32 @@ export default function PromoDetailDialog({
} catch (err) { } catch (err) {
console.error("ERROR FETCH PROMO:", err); console.error("ERROR FETCH PROMO:", err);
} finally { } finally {
setLoading(false); setLoadingData(false);
} }
} }
fetchData(); fetchData();
}, [promoId, open]); }, [promoId, open]);
const [openCommentModal, setOpenCommentModal] = useState(false);
const [commentValue, setCommentValue] = useState("");
const [openViewDialog, setOpenViewDialog] = useState(false);
if (!open) return null; if (!open) return null;
const handleSubmitComment = async () => {
// await api.post("/banner/comment", {
// bannerId: promo.id,
// comment: commentValue,
// });
setOpenCommentModal(false);
};
return ( return (
<> <>
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="p-0 max-w-xl overflow-hidden rounded-2xl"> <DialogContent className="p-0 max-w-lg overflow-hidden">
{/* HEADER */} {/* HEADER */}
<div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative"> <div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative">
<DialogHeader> <DialogHeader>
@ -107,7 +162,7 @@ export default function PromoDetailDialog({
{/* BODY */} {/* BODY */}
<div className="p-6"> <div className="p-6">
{loading ? ( {loadingData ? (
<p className="text-Start text-gray-600">Memuat data...</p> <p className="text-Start text-gray-600">Memuat data...</p>
) : promo ? ( ) : promo ? (
<> <>
@ -133,28 +188,61 @@ export default function PromoDetailDialog({
</div> </div>
{/* TIMELINE */} {/* TIMELINE */}
<div className="mt-4"> <div>
<p className="text-gray-600 mb-3 font-medium"> <h4 className="text-sm font-semibold text-gray-700 mb-3">
Status Timeline Status Timeline
</p> </h4>
<div className="space-y-4"> <div className="space-y-4">
{promo.timeline.map((item: any, idx: number) => ( <div className="flex gap-3">
<div key={idx} className="flex gap-3 items-start"> <div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
<CheckCircle2 <Check className="w-4 h-4 text-green-600" />
size={20} </div>
className="text-green-600 shrink-0"
/>
<div> <div>
<p className="font-medium">{item.label}</p> <p className="font-medium text-gray-800">
<p className="text-gray-600 text-sm"> Diupload oleh Operator
{item.date} {item.time} WIB </p>
<p className="text-sm text-gray-500">
{convertDateFormat(promo?.created_at)} WIB
</p> </p>
</div> </div>
</div> </div>
))}
<div className="flex gap-3">
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
promo?.status_id === 1
? "bg-yellow-100"
: promo?.status_id === 2
? "bg-green-100"
: "bg-red-100"
}`}
>
{promo?.status_id === 1 ? (
<Clock className="w-4 h-4 text-yellow-700" />
) : promo?.status_id === 2 ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-red-700" />
)}
</div> </div>
<div className="border rounded-lg px-3 py-3 mt-3">
<div>
<p className="font-medium text-gray-800">
{promo?.status_id === 1
? "Menunggu disetujui oleh Approver"
: promo?.status_id === 2
? "Disetujui oleh Approver"
: "Ditolak oleh Approver"}
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(promo?.updated_at)} WIB
</p>
</div>
</div>
<div className="border rounded-lg px-3 py-3">
<p>Comment : </p> <p>Comment : </p>
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<button <button
@ -168,11 +256,67 @@ export default function PromoDetailDialog({
</div> </div>
</div> </div>
</div> </div>
</div>
</> </>
) : ( ) : (
<p className="text-center text-gray-600">Data tidak ditemukan</p> <p className="text-center text-gray-600">Data tidak ditemukan</p>
)} )}
</div> </div>
{userLevelId !== "2" && promo && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{promo.status_id === 1 ? (
<>
<Button
variant="secondary"
className="bg-blue-200 hover:bg-blue-400"
onClick={(e) => {
e.stopPropagation();
setOpenCommentModal(true);
}}
>
Beri Tanggapan
</Button>
<Button
className=" w-[150]"
variant="destructive"
// onClick={(e) => {
// e.stopPropagation();
// handleReject();
// }}
>
Reject
</Button>
{userLevelId === "1" && (
<Button
// variant="ghost"
size="sm"
className="bg-green-600 hover:bg-green-700 text-white w-[150]"
onClick={(e) => {
e.stopPropagation();
handleApprovePromotion(promo.id);
}}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)}
</>
) : (
<Button
variant="secondary"
className="mx-auto"
onClick={(e) => {
e.stopPropagation();
setOpenViewDialog(false);
}}
>
Tutup
</Button>
)}
</div>
)}
{openApproverHistory && ( {openApproverHistory && (
<div <div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4" className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
@ -271,7 +415,7 @@ export default function PromoDetailDialog({
</div> </div>
</div> </div>
)} )}
{/* FOOTER */} {/* FOOTER
<div className="bg-[#E3EFF4] text-center py-3"> <div className="bg-[#E3EFF4] text-center py-3">
<button <button
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
@ -279,7 +423,7 @@ export default function PromoDetailDialog({
> >
Tutup Tutup
</button> </button>
</div> </div> */}
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{openApproverHistory && ( {openApproverHistory && (
@ -380,6 +524,76 @@ export default function PromoDetailDialog({
</div> </div>
</div> </div>
)} )}
{openCommentModal && promo && (
<div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenCommentModal(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-lg w-full overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */}
<div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
<button
onClick={() => setOpenCommentModal(false)}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Beri Tanggapan</h2>
{/* Badge */}
<div className="flex items-center gap-2 mt-3">
<span className="bg-yellow-100 text-yellow-800 text-xs font-medium px-3 py-1 rounded-full">
Menunggu
</span>
<span className="bg-white text-[#0F6C75] text-xs font-medium px-3 py-1 rounded-full">
Banner
</span>
<span className="bg-white/20 text-white text-xs px-2 py-[2px] rounded-full">
{promo.position}
</span>
</div>
</div>
{/* BODY */}
<div className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-gray-500 mb-2">
Comment
</label>
<textarea
value={commentValue}
onChange={(e) => setCommentValue(e.target.value)}
placeholder="Masukkan komentar"
className="w-full min-h-[100px] border rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-[#0F6C75]"
/>
</div>
</div>
{/* FOOTER */}
<div className="flex justify-between gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
<button
onClick={() => setOpenCommentModal(false)}
className="flex-1 py-2 rounded-xl bg-blue-100 hover:bg-blue-200 text-gray-700 font-medium"
>
Batal
</button>
<button
onClick={handleSubmitComment}
className="flex-1 py-2 rounded-xl bg-[#1F6779] hover:bg-[#0F6C75] text-white font-medium"
>
Submit
</button>
</div>
</div>
</div>
)}
</> </>
); );
} }

View File

@ -1,7 +1,15 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Upload, Plus, Settings, CheckCheck } from "lucide-react"; import {
Upload,
Plus,
Settings,
CheckCheck,
Check,
Clock,
X,
} from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@ -14,17 +22,18 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { loading } from "@/config/swal"; import { error, loading, success } from "@/config/swal";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import z from "zod"; import z from "zod";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useParams } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { getProductDataById } from "@/service/product"; import { getProductDataById } from "@/service/product";
import { getAgentById } from "@/service/agent"; import { approveAgent, getAgentById, updateAgent } from "@/service/agent";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { convertDateFormat } from "@/utils/global";
const ViewEditor = dynamic( const ViewEditor = dynamic(
() => { () => {
@ -85,7 +94,25 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
const id = params?.id; const id = params?.id;
const [data, setData] = useState<any>(null); const [data, setData] = useState<any>(null);
const [openApproverHistory, setOpenApproverHistory] = useState(false); const [openApproverHistory, setOpenApproverHistory] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const router = useRouter();
const [openCommentModal, setOpenCommentModal] = useState(false);
const [commentValue, setCommentValue] = useState("");
const handleSubmitComment = async () => {
// await api.post("/banner/comment", {
// bannerId: viewBanner.id,
// comment: commentValue,
// });
setOpenCommentModal(false);
};
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
}, []);
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, []); }, []);
@ -101,6 +128,41 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
setOpenApproverHistory(true); setOpenApproverHistory(true);
}; };
// const handleRejectProduct = async (id: number) => {
// loading();
// const payload = {
// status_id: 3, // ✅ number
// };
// const res = await updateAgent(payload, id);
// if (res?.error) {
// error(res.message || "Gagal menolak product");
// close();
// return;
// }
// close();
// success("Product berhasil ditolak");
// fetchData();
// };
const handleApproveAgent = async (id: number) => {
loading();
const res = await approveAgent(id);
if (res?.error) {
error(res.message || "Gagal menyetujui agent");
close();
return;
}
close();
success("Agent berhasil disetujui");
fetchData(); // refresh table
};
return ( return (
<> <>
<Card className="w-full border-none shadow-md"> <Card className="w-full border-none shadow-md">
@ -155,6 +217,60 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
/> />
</div> </div>
</div> </div>
<div>
<h4 className="text-sm font-semibold text-gray-700 mb-3">
Status Timeline
</h4>
<div className="space-y-4">
<div className="flex gap-3">
<div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
<Check className="w-4 h-4 text-green-600" />
</div>
<div>
<p className="font-medium text-gray-800">
Diupload oleh Operator
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(data.created_at)} WIB
</p>
</div>
</div>
</div>
<div className="flex gap-3">
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
data?.status_id === 1
? "bg-yellow-100"
: data?.status_id === 2
? "bg-green-100"
: "bg-red-100"
}`}
>
{data?.status_id === 1 ? (
<Clock className="w-4 h-4 text-yellow-700" />
) : data?.status_id === 2 ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-red-700" />
)}
</div>
<div>
<p className="font-medium text-gray-800">
{data?.status_id === 1
? "Menunggu disetujui oleh Approver"
: data?.status_id === 2
? "Disetujui oleh Approver"
: "Ditolak oleh Approver"}
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(data?.updated_at)} WIB
</p>
</div>
</div>
</div>
<div className="border rounded-lg px-3 py-3"> <div className="border rounded-lg px-3 py-3">
<p>Comment : </p> <p>Comment : </p>
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
@ -167,6 +283,47 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
<p>Jaecoo - Approver | 10/11/2026</p> <p>Jaecoo - Approver | 10/11/2026</p>
</div> </div>
</div> </div>
{userLevelId !== "2" && data && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA] mt-10">
{data.status_id === 1 ? (
<>
<Button
variant="secondary"
className="bg-blue-200 hover:bg-blue-400"
onClick={() => setOpenCommentModal(true)}
>
Beri Tanggapan
</Button>
<Button
variant="destructive"
className="w-[180px]"
// onClick={() => handleRejectProduct(detailData.id)}
>
Reject
</Button>
{userLevelId === "1" && (
<Button
className="bg-green-600 hover:bg-green-700 text-white w-[180px]"
onClick={() => handleApproveAgent(data.id)}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)}
</>
) : (
<Button
variant="secondary"
className="mx-auto"
onClick={() => router.back()}
>
Tutup
</Button>
)}
</div>
)}
{/* <Button className=" bg-teal-800 hover:bg-teal-900 text-white py-3"> {/* <Button className=" bg-teal-800 hover:bg-teal-900 text-white py-3">
Submit Submit
</Button> */} </Button> */}

View File

@ -1,7 +1,15 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Upload, Plus, Settings, CheckCheck } from "lucide-react"; import {
Upload,
Plus,
Settings,
CheckCheck,
Clock,
Check,
X,
} from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@ -14,15 +22,20 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { loading } from "@/config/swal"; import { error, loading, success } from "@/config/swal";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import z from "zod"; import z from "zod";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useParams } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { getProductDataById } from "@/service/product"; import {
approveProduct,
getProductDataById,
updateProduct,
} from "@/service/product";
import { convertDateFormat } from "@/utils/global";
const ViewEditor = dynamic( const ViewEditor = dynamic(
() => { () => {
@ -99,6 +112,15 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
const [startDateValue, setStartDateValue] = useState<any>(null); const [startDateValue, setStartDateValue] = useState<any>(null);
const [timeValue, setTimeValue] = useState("00:00"); const [timeValue, setTimeValue] = useState("00:00");
const [openApproverHistory, setOpenApproverHistory] = useState(false); const [openApproverHistory, setOpenApproverHistory] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const router = useRouter();
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
}, []);
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => { onDrop: (acceptedFiles) => {
@ -199,6 +221,17 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
}; };
const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); 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<{ const [uploadTarget, setUploadTarget] = useState<{
type: "spec" | "color"; type: "spec" | "color";
index: number; index: number;
@ -268,6 +301,41 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
} }
} }
const handleRejectProduct = async (id: number) => {
loading();
const payload = {
status_id: 3, // ✅ number
};
const res = await updateProduct(payload, id);
if (res?.error) {
error(res.message || "Gagal menolak product");
close();
return;
}
close();
success("Product berhasil ditolak");
initState();
};
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 = () => { const handleOpenApproverHistory = () => {
setOpenApproverHistory(true); setOpenApproverHistory(true);
}; };
@ -517,21 +585,56 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
</Button> */} </Button> */}
</div> </div>
<div> <div>
<p>Status Timeline</p> <h4 className="text-sm font-semibold text-gray-700 mb-3">
Status Timeline
</h4>
<div className="space-y-4">
<div className="flex gap-3"> <div className="flex gap-3">
<div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center"> <div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
<CheckCheck className="w-4 h-4 text-green-600" /> <Check className="w-4 h-4 text-green-600" />
</div> </div>
<div> <div>
<p className="font-medium text-gray-800">Diupload oleh</p> <p className="font-medium text-gray-800">
Diupload oleh Operator
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(detailData?.created_at)} WIB
</p>
</div> </div>
</div> </div>
<div className="flex gap-3 mt-3">
<div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
<CheckCheck className="w-4 h-4 text-green-600" />
</div> </div>
<div className="flex gap-3">
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
detailData?.status_id === 1
? "bg-yellow-100"
: detailData?.status_id === 2
? "bg-green-100"
: "bg-red-100"
}`}
>
{detailData?.status_id === 1 ? (
<Clock className="w-4 h-4 text-yellow-700" />
) : detailData?.status_id === 2 ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-red-700" />
)}
</div>
<div> <div>
<p className="font-medium text-gray-800">Disetujui oleh</p> <p className="font-medium text-gray-800">
{detailData?.status_id === 1
? "Menunggu disetujui oleh Approver"
: detailData?.status_id === 2
? "Disetujui oleh Approver"
: "Ditolak oleh Approver"}
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(detailData?.updated_at)} WIB
</p>
</div> </div>
</div> </div>
</div> </div>
@ -547,6 +650,48 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
<p>Jaecoo - Approver | 10/11/2026</p> <p>Jaecoo - Approver | 10/11/2026</p>
</div> </div>
</div> </div>
{userLevelId !== "2" && detailData && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA] mt-10">
{detailData.status_id === 1 ? (
<>
<Button
variant="secondary"
className="bg-blue-200 hover:bg-blue-400"
onClick={() => setOpenCommentModal(true)}
>
Beri Tanggapan
</Button>
<Button
variant="destructive"
className="w-[180px]"
onClick={() => handleRejectProduct(detailData.id)}
>
Reject
</Button>
{userLevelId === "1" && (
<Button
className="bg-green-600 hover:bg-green-700 text-white w-[180px]"
onClick={() => handleApproveProduct(detailData.id)}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)}
</>
) : (
<Button
variant="secondary"
className="mx-auto"
onClick={() => router.back()}
>
Tutup
</Button>
)}
</div>
)}
{/* <Button className=" bg-teal-800 hover:bg-teal-900 text-white py-3"> {/* <Button className=" bg-teal-800 hover:bg-teal-900 text-white py-3">
Submit Submit
</Button> */} </Button> */}
@ -695,6 +840,76 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{openCommentModal && detailData && (
<div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenCommentModal(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-lg w-full overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */}
<div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
<button
onClick={() => setOpenCommentModal(false)}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Beri Tanggapan</h2>
{/* Badge */}
<div className="flex items-center gap-2 mt-3">
<span className="bg-yellow-100 text-yellow-800 text-xs font-medium px-3 py-1 rounded-full">
Menunggu
</span>
<span className="bg-white text-[#0F6C75] text-xs font-medium px-3 py-1 rounded-full">
Banner
</span>
<span className="bg-white/20 text-white text-xs px-2 py-[2px] rounded-full">
{detailData.position}
</span>
</div>
</div>
{/* BODY */}
<div className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-gray-500 mb-2">
Comment
</label>
<textarea
value={commentValue}
onChange={(e) => setCommentValue(e.target.value)}
placeholder="Masukkan komentar"
className="w-full min-h-[100px] border rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-[#0F6C75]"
/>
</div>
</div>
{/* FOOTER */}
<div className="flex justify-between gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
<button
onClick={() => setOpenCommentModal(false)}
className="flex-1 py-2 rounded-xl bg-blue-100 hover:bg-blue-200 text-gray-700 font-medium"
>
Batal
</button>
<button
onClick={handleSubmitComment}
className="flex-1 py-2 rounded-xl bg-[#1F6779] hover:bg-[#0F6C75] text-white font-medium"
>
Submit
</button>
</div>
</div>
</div>
)}
</> </>
); );
} }

View File

@ -416,6 +416,10 @@ export default function AgentTable() {
<span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium">
Disetujui Disetujui
</span> </span>
) : item.status_id === 3 ? (
<span className="bg-red-100 text-red-700 text-xs px-3 py-1 rounded-full font-medium">
Ditolak
</span>
) : ( ) : (
<span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium">
{item.status_id || "Tidak Diketahui"} {item.status_id || "Tidak Diketahui"}
@ -448,6 +452,7 @@ export default function AgentTable() {
</Button> </Button>
</Link> </Link>
{/* Tombol Edit */} {/* Tombol Edit */}
{userLevelId !== "1" && (
<Link href={`/admin/agent/update/${item.id}`}> <Link href={`/admin/agent/update/${item.id}`}>
<Button <Button
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0" className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"
@ -457,9 +462,9 @@ export default function AgentTable() {
<CreateIconIon className="w-4 h-4 mr-1" /> Edit <CreateIconIon className="w-4 h-4 mr-1" /> Edit
</Button> </Button>
</Link> </Link>
)}
{/* Tombol Approve - hanya untuk admin dan status pending */} {/* Tombol Approve - hanya untuk admin dan status pending */}
{userLevelId === "1" && item.status_id === 1 && ( {/* {userLevelId === "1" && item.status_id === 1 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -469,7 +474,7 @@ export default function AgentTable() {
<CheckCheck className="w-4 h-4 mr-1" /> <CheckCheck className="w-4 h-4 mr-1" />
Approve Approve
</Button> </Button>
)} )} */}
{/* Tombol Hapus */} {/* Tombol Hapus */}
<Button <Button
variant="ghost" variant="ghost"

View File

@ -46,7 +46,12 @@ import {
} from "@/components/ui/table"; } from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination"; import CustomPagination from "../layout/custom-pagination";
import { EditBannerDialog } from "../form/banner-edit-dialog"; import { EditBannerDialog } from "../form/banner-edit-dialog";
import { deleteBanner, getBannerData, updateBanner, approveBanner } from "@/service/banner"; import {
deleteBanner,
getBannerData,
updateBanner,
approveBanner,
} from "@/service/banner";
import { Check, CheckCheck, Clock, Eye, X } from "lucide-react"; import { Check, CheckCheck, Clock, Eye, X } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -219,25 +224,25 @@ export default function ArticleTable() {
await updateBanner(formData, id); await updateBanner(formData, id);
}; };
const handleApprove = async () => { // const handleApprove = async () => {
if (!viewBanner) return; // if (!viewBanner) return;
loading(); // loading();
const formData = new FormData(); // const formData = new FormData();
formData.append("status", "2"); // APPROVED // formData.append("status", "2"); // APPROVED
const res = await updateBanner(formData, viewBanner.id); // const res = await updateBanner(formData, viewBanner.id);
if (res?.error) { // if (res?.error) {
error(res.message); // error(res.message);
return; // return;
} // }
close(); // close();
success("Banner berhasil disetujui"); // success("Banner berhasil disetujui");
setOpenViewDialog(false); // setOpenViewDialog(false);
initState(); // refresh table // initState(); // refresh table
}; // };
const handleApproveBanner = async (id: number) => { const handleApproveBanner = async (id: number) => {
loading(); loading();
@ -486,6 +491,10 @@ export default function ArticleTable() {
<span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium">
Disetujui Disetujui
</span> </span>
) : item.status_id === 3 ? (
<span className="bg-red-100 text-red-700 text-xs px-3 py-1 rounded-full font-medium">
Ditolak
</span>
) : ( ) : (
<span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium">
{item.status_id || "Tidak Diketahui"} {item.status_id || "Tidak Diketahui"}
@ -505,7 +514,7 @@ export default function ArticleTable() {
<Eye className="w-4 h-4 mr-1" /> <Eye className="w-4 h-4 mr-1" />
Lihat Lihat
</Button> </Button>
{userLevelId !== "3" && ( {userLevelId !== "1" && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -516,7 +525,7 @@ export default function ArticleTable() {
Edit Edit
</Button> </Button>
)} )}
{userLevelId === "1" && item.status_id === 1 && ( {/* {userLevelId === "1" && item.status_id === 1 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -526,7 +535,7 @@ export default function ArticleTable() {
<CheckCheck className="w-4 h-4 mr-1" /> <CheckCheck className="w-4 h-4 mr-1" />
Approve Approve
</Button> </Button>
)} )} */}
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -669,21 +678,21 @@ export default function ArticleTable() {
<div className="flex items-center gap-2 mt-3"> <div className="flex items-center gap-2 mt-3">
<span <span
className={`text-xs font-medium px-3 py-1 rounded-full ${ className={`text-xs font-medium px-3 py-1 rounded-full ${
viewBanner.status === "1" viewBanner.status_id === 1
? "bg-yellow-100 text-yellow-800" ? "bg-yellow-100 text-yellow-800"
: viewBanner.status === "2" : viewBanner.status_id === 2
? "bg-green-100 text-green-800" ? "bg-green-100 text-green-800"
: viewBanner.status === "3" : viewBanner.status_id === 3
? "bg-red-100 text-red-800" ? "bg-red-100 text-red-800"
: "bg-gray-100 text-gray-800" : "bg-gray-100 text-gray-800"
}`} }`}
> >
{viewBanner.status === "1" {viewBanner.status_id === 1
? "Menunggu" ? "Menunggu"
: viewBanner.status === "2" : viewBanner.status_id === 2
? "Disetujui" ? "Disetujui"
: viewBanner.status === "3" : viewBanner.status_id === 3
? "Reject" ? "Ditolak"
: "Tidak Diketahui"} : "Tidak Diketahui"}
</span> </span>
@ -753,16 +762,16 @@ export default function ArticleTable() {
<div className="flex gap-3"> <div className="flex gap-3">
<div <div
className={`w-6 h-6 rounded-full flex items-center justify-center ${ className={`w-6 h-6 rounded-full flex items-center justify-center ${
viewBanner.status === "1" viewBanner.status_id === 1
? "bg-yellow-100" ? "bg-yellow-100"
: viewBanner.status === "2" : viewBanner.status_id === 2
? "bg-green-100" ? "bg-green-100"
: "bg-red-100" : "bg-red-100"
}`} }`}
> >
{viewBanner.status === "1" ? ( {viewBanner.status_id === 1 ? (
<Clock className="w-4 h-4 text-yellow-700" /> <Clock className="w-4 h-4 text-yellow-700" />
) : viewBanner.status === "2" ? ( ) : viewBanner.status_id === 2 ? (
<Check className="w-4 h-4 text-green-600" /> <Check className="w-4 h-4 text-green-600" />
) : ( ) : (
<X className="w-4 h-4 text-red-700" /> <X className="w-4 h-4 text-red-700" />
@ -771,9 +780,9 @@ export default function ArticleTable() {
<div> <div>
<p className="font-medium text-gray-800"> <p className="font-medium text-gray-800">
{viewBanner.status === "1" {viewBanner.status_id === 1
? "Menunggu disetujui oleh Approver" ? "Menunggu disetujui oleh Approver"
: viewBanner.status === "2" : viewBanner.status_id === 2
? "Disetujui oleh Approver" ? "Disetujui oleh Approver"
: "Ditolak oleh Approver"} : "Ditolak oleh Approver"}
</p> </p>
@ -803,7 +812,7 @@ export default function ArticleTable() {
{/* FOOTER */} {/* FOOTER */}
{userLevelId !== "2" && ( {userLevelId !== "2" && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]"> <div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{viewBanner.status === "1" ? ( {viewBanner.status_id === 1 ? (
<> <>
<Button <Button
variant="secondary" variant="secondary"
@ -817,8 +826,8 @@ export default function ArticleTable() {
</Button> </Button>
<Button <Button
className=" w-[180]"
variant="destructive" variant="destructive"
className="w-[180]"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleReject(); handleReject();
@ -827,15 +836,20 @@ export default function ArticleTable() {
Reject Reject
</Button> </Button>
{userLevelId === "1" && (
<Button <Button
// variant="ghost"
size="sm"
className="bg-green-600 hover:bg-green-700 text-white w-[180]" className="bg-green-600 hover:bg-green-700 text-white w-[180]"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleApprove(); handleApproveBanner(viewBanner.id);
}} }}
> >
Approved <CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button> </Button>
)}
</> </>
) : ( ) : (
<Button <Button

View File

@ -2,7 +2,14 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { Eye, Pencil, Trash2, Calendar, MapPin, CheckCheck } from "lucide-react"; import {
Eye,
Pencil,
Trash2,
Calendar,
MapPin,
CheckCheck,
} from "lucide-react";
import { import {
deleteGalery, deleteGalery,
getGaleryById, getGaleryById,
@ -50,7 +57,7 @@ export default function Galery() {
// Filter file dengan gallery_id sama dengan item.id // Filter file dengan gallery_id sama dengan item.id
const filteredImages = images?.filter( const filteredImages = images?.filter(
(img: any) => img.gallery_id === item.id (img: any) => img.gallery_id === item.id,
); );
// Ambil gambar pertama sebagai thumbnail // Ambil gambar pertama sebagai thumbnail
@ -64,7 +71,7 @@ export default function Galery() {
} catch (e) { } catch (e) {
return { ...item, image_url: null }; return { ...item, image_url: null };
} }
}) }),
); );
setData(merged); setData(merged);
@ -211,23 +218,23 @@ export default function Galery() {
> >
<Eye className="h-4 w-4" /> Lihat <Eye className="h-4 w-4" /> Lihat
</button> </button>
{userLevelId !== "1" && (
<button <button
onClick={() => openEdit(item.id)} onClick={() => openEdit(item.id)}
className="flex items-center gap-1 text-gray-700 hover:text-[#1F6779] transition" className="flex items-center gap-1 text-gray-700 hover:text-[#1F6779] transition"
> >
<Pencil className="h-4 w-4" /> Edit <Pencil className="h-4 w-4" /> Edit
</button> </button>
)}
{/* Tombol Approve - hanya untuk admin dan status pending */} {/* Tombol Approve - hanya untuk admin dan status pending */}
{userLevelId === "1" && item.status_id === 1 && ( {/* {userLevelId === "1" && item.status_id === 1 && (
<button <button
className="flex items-center gap-1 text-green-600 hover:text-green-700 transition" className="flex items-center gap-1 text-green-600 hover:text-green-700 transition"
onClick={() => handleApproveGalery(item.id)} onClick={() => handleApproveGalery(item.id)}
> >
<CheckCheck className="h-4 w-4" /> Approve <CheckCheck className="h-4 w-4" /> Approve
</button> </button>
)} )} */}
<button <button
className="flex items-center gap-1 text-red-600 hover:text-red-700 transition" className="flex items-center gap-1 text-red-600 hover:text-red-700 transition"

View File

@ -47,7 +47,11 @@ import {
} from "@/components/ui/table"; } from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination"; import CustomPagination from "../layout/custom-pagination";
import { EditBannerDialog } from "../form/banner-edit-dialog"; import { EditBannerDialog } from "../form/banner-edit-dialog";
import { deleteProduct, getProductPagination, approveProduct } from "@/service/product"; import {
deleteProduct,
getProductPagination,
approveProduct,
} from "@/service/product";
import { CheckCheck, Eye } from "lucide-react"; import { CheckCheck, Eye } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -448,6 +452,10 @@ export default function ProductTable() {
<span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium">
Disetujui Disetujui
</span> </span>
) : item.status_id === 3 ? (
<span className="bg-red-100 text-red-700 text-xs px-3 py-1 rounded-full font-medium">
Ditolak
</span>
) : ( ) : (
<span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium"> <span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium">
{item.status_id || "Tidak Diketahui"} {item.status_id || "Tidak Diketahui"}
@ -468,7 +476,7 @@ export default function ProductTable() {
Lihat Lihat
</Button> </Button>
</Link> </Link>
{userLevelId !== "3" && ( {userLevelId !== "1" && (
<Link href={"/admin/product/update"}> <Link href={"/admin/product/update"}>
<Button <Button
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0" className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"
@ -479,7 +487,7 @@ export default function ProductTable() {
</Button> </Button>
</Link> </Link>
)} )}
{userLevelId === "1" && item.status_id === 1 && ( {/* {userLevelId === "1" && item.status_id === 1 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -489,7 +497,7 @@ export default function ProductTable() {
<CheckCheck className="w-4 h-4 mr-1" /> <CheckCheck className="w-4 h-4 mr-1" />
Approve Approve
</Button> </Button>
)} )} */}
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"

View File

@ -50,7 +50,11 @@ import { EditBannerDialog } from "../form/banner-edit-dialog";
import { Eye, Trash2, CheckCheck } from "lucide-react"; import { Eye, Trash2, CheckCheck } from "lucide-react";
import AgentDetailDialog from "../dialog/agent-dialog"; import AgentDetailDialog from "../dialog/agent-dialog";
import PromoDetailDialog from "../dialog/promo-dialog"; import PromoDetailDialog from "../dialog/promo-dialog";
import { deletePromotion, getPromotionPagination, approvePromotion } from "@/service/promotion"; import {
deletePromotion,
getPromotionPagination,
approvePromotion,
} from "@/service/promotion";
const columns = [ const columns = [
{ name: "No", uid: "no" }, { name: "No", uid: "no" },
@ -433,7 +437,7 @@ export default function PromotionTable() {
{/* Tombol Edit */} {/* Tombol Edit */}
{/* Tombol Approve - hanya untuk admin dan status pending */} {/* Tombol Approve - hanya untuk admin dan status pending */}
{userLevelId === "1" && item.status_id === 1 && ( {/* {userLevelId === "1" && item.status_id === 1 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -443,7 +447,7 @@ export default function PromotionTable() {
<CheckCheck className="w-4 h-4 mr-1" /> <CheckCheck className="w-4 h-4 mr-1" />
Approve Approve
</Button> </Button>
)} )} */}
{/* Tombol Hapus */} {/* Tombol Hapus */}
<Button <Button
variant="ghost" variant="ghost"