fix: table agent,banner,product,promo,galery, form and dialog

This commit is contained in:
Anang Yusman 2026-01-20 17:22:45 +08:00
commit f516565446
20 changed files with 629 additions and 145 deletions

View File

@ -16,15 +16,15 @@ import { useRouter } from "next/navigation";
export default function AgentPage() {
const [openDialog, setOpenDialog] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
const MySwal = withReactContent(Swal);
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // contoh: "3"
setUserRoleId(urie ?? null);
}, []);
const handleSubmitBanner = (data: any) => {
@ -41,7 +41,7 @@ export default function AgentPage() {
</div>
<div className="dark:bg-[#18181b] rounded-xl p-3">
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Link href={"/admin/agent/create"}>
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
<Plus className="h-4 w-4" />

View File

@ -15,15 +15,15 @@ import Cookies from "js-cookie";
export default function BasicPage() {
const [openDialog, setOpenDialog] = useState(false);
const [refreshKey, setRefreshKey] = useState(0);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
const MySwal = withReactContent(Swal);
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const ulne = Cookies.get("urie"); // contoh: "3"
setUserRoleId(ulne ?? null);
}, []);
const handleSubmitBanner = async (formData: FormData) => {
@ -58,7 +58,7 @@ export default function BasicPage() {
</div>
<div className="dark:bg-[#18181b] rounded-xl p-3">
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Button
className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2"
onClick={() => setOpenDialog(true)}

View File

@ -13,15 +13,15 @@ import Cookies from "js-cookie";
export default function GaleryPage() {
const [openDialog, setOpenDialog] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
const MySwal = withReactContent(Swal);
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // contoh: "3"
setUserRoleId(urie ?? null);
}, []);
const handleSubmitGaleri = () => {
@ -39,7 +39,7 @@ export default function GaleryPage() {
</div>
<div className="dark:bg-[#18181b] rounded-xl p-3">
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Button
className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2"
onClick={() => setOpenDialog(true)}

View File

@ -14,15 +14,15 @@ import Cookies from "js-cookie";
export default function ProductPage() {
const [openDialog, setOpenDialog] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
const MySwal = withReactContent(Swal);
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // contoh: "3"
setUserRoleId(urie ?? null);
}, []);
const handleSubmitBanner = (data: any) => {
@ -40,7 +40,7 @@ export default function ProductPage() {
</div>
<div className="dark:bg-[#18181b] rounded-xl p-3">
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Link href={"/admin/product/create"}>
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
<Plus className="h-4 w-4" />

View File

@ -17,15 +17,15 @@ import Cookies from "js-cookie";
export default function PromotionPage() {
const [openDialog, setOpenDialog] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
const MySwal = withReactContent(Swal);
// 🔹 Ambil userlevelId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // contoh: "3"
setUserRoleId(urie ?? null);
}, []);
const handleSubmitBanner = (data: any) => {
@ -43,7 +43,7 @@ export default function PromotionPage() {
</div>
<div className="dark:bg-[#18181b] rounded-xl p-3">
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Link href={"/admin/promotion/create"}>
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
<Plus className="h-4 w-4" />

View File

@ -10,24 +10,30 @@ import {
import Image from "next/image";
import { Check, CheckCheck, CheckCircle, Clock, X } from "lucide-react";
import { useEffect, useState } from "react";
import { approveGalery, getGaleryFileData } from "@/service/galery";
import {
approveGalery,
getGaleryFileData,
rejectGalery,
} 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";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
export function DialogDetailGaleri({ open, onClose, data }: any) {
const [images, setImages] = useState<any[]>([]);
const [openApproverHistory, setOpenApproverHistory] = useState(false);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = 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 urie = Cookies.get("urie"); // contoh: "3"
setUserRoleId(urie ?? null);
}, []);
const [openCommentModal, setOpenCommentModal] = useState(false);
@ -84,6 +90,40 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
fetchImages(); // refresh table
};
const handleRejectGalery = async (id: number) => {
const MySwal = withReactContent(Swal);
const { value: message } = await MySwal.fire({
title: "Tolak Galery",
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 rejectGalery(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak galery");
close();
return;
}
close();
success("Galery berhasil ditolak");
fetchImages(); // refresh table
};
return (
<>
<Dialog open={open} onOpenChange={onClose}>
@ -214,7 +254,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
</div>
</div>
</div>
{openViewDialog && userLevelId !== "2" && (
{userRoleId !== "2" && data && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{data.status_id === 1 ? (
<>
@ -232,15 +272,15 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
<Button
className=" w-[180]"
variant="destructive"
// onClick={(e) => {
// e.stopPropagation();
// handleReject();
// }}
onClick={(e) => {
e.stopPropagation();
handleRejectGalery(data.id);
}}
>
Reject
</Button>
{userLevelId === "1" && (
{userRoleId === "1" && (
<Button
// variant="ghost"
size="sm"

View File

@ -15,12 +15,18 @@ import {
FileText,
X,
} from "lucide-react";
import { approvePromotion, getPromotionById } from "@/service/promotion";
import {
approvePromotion,
getPromotionById,
rejectPromotion,
} 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";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
type PromoDetailDialogProps = {
promoId: number | null;
@ -60,6 +66,40 @@ export default function PromoDetailDialog({
setOpenApproverHistory(true);
};
const handleRejectPromotion = async (id: number) => {
const MySwal = withReactContent(Swal);
const { value: message } = await MySwal.fire({
title: "Tolak Promotion",
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 rejectPromotion(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak promotion");
close();
return;
}
close();
success("Promotion berhasil ditolak");
// initState(); // refresh table
};
const handleApprovePromotion = async (promoId: number) => {
loading();
const res = await approvePromotion(promoId);
@ -280,10 +320,10 @@ export default function PromoDetailDialog({
<Button
className=" w-[150]"
variant="destructive"
// onClick={(e) => {
// e.stopPropagation();
// handleReject();
// }}
onClick={(e) => {
e.stopPropagation();
handleRejectPromotion(promo.id);
}}
>
Reject
</Button>
@ -319,7 +359,7 @@ export default function PromoDetailDialog({
)}
{openApproverHistory && (
<div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
className=" flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenApproverHistory(false)}
>
<div

View File

@ -31,9 +31,16 @@ import dynamic from "next/dynamic";
import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { getProductDataById } from "@/service/product";
import { approveAgent, getAgentById, updateAgent } from "@/service/agent";
import {
approveAgent,
getAgentById,
rejectAgent,
updateAgent,
} from "@/service/agent";
import { Checkbox } from "@/components/ui/checkbox";
import { convertDateFormat } from "@/utils/global";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
const ViewEditor = dynamic(
() => {
@ -128,25 +135,39 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
setOpenApproverHistory(true);
};
// const handleRejectProduct = async (id: number) => {
// loading();
const handleRejectAgent = async (id: number) => {
const MySwal = withReactContent(Swal);
const { value: message } = await MySwal.fire({
title: "Tolak Agent",
input: "textarea",
inputLabel: "Alasan penolakan (opsional)",
inputPlaceholder: "Masukkan alasan penolakan...",
inputAttributes: {
"aria-label": "Masukkan alasan penolakan",
},
showCancelButton: true,
confirmButtonText: "Tolak",
cancelButtonText: "Batal",
confirmButtonColor: "#dc2626",
});
// const payload = {
// status_id: 3, // ✅ number
// };
if (message === undefined) {
return; // User cancelled
}
// const res = await updateAgent(payload, id);
loading();
const res = await rejectAgent(id, message || undefined);
// if (res?.error) {
// error(res.message || "Gagal menolak product");
// close();
// return;
// }
if (res?.error) {
error(res.message || "Gagal menolak agent");
close();
return;
}
// close();
// success("Product berhasil ditolak");
// fetchData();
// };
close();
success("Agent berhasil ditolak");
fetchData(); // refresh table
};
const handleApproveAgent = async (id: number) => {
loading();
@ -298,7 +319,7 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
<Button
variant="destructive"
className="w-[180px]"
// onClick={() => handleRejectProduct(detailData.id)}
onClick={() => handleRejectAgent(data.id)}
>
Reject
</Button>

View File

@ -33,9 +33,12 @@ 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(
() => {
@ -302,13 +305,27 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
}
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 payload = {
status_id: 3, // ✅ number
};
const res = await updateProduct(payload, id);
const res = await rejectProduct(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak product");
@ -318,7 +335,7 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
close();
success("Product berhasil ditolak");
initState();
initState(); // refresh table
};
const handleApproveProduct = async (id: number) => {

View File

@ -6,12 +6,15 @@ import Image from "next/image";
import { Icon } from "@iconify/react";
import Link from "next/link";
import DashboardContainer from "../main/dashboard/dashboard-container";
import { usePathname } from "next/navigation";
import { usePathname, useRouter } from "next/navigation";
import { motion, AnimatePresence } from "framer-motion";
import { useTheme } from "../layout/theme-context";
import Option from "./option";
import { LogOut } from "lucide-react";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import Cookies from "js-cookie";
interface RetractingSidebarProps {
sidebarData: boolean;
@ -87,6 +90,7 @@ const sidebarSections = [
),
link: "/admin/costumer-service",
},
{
title: "Manajemen User",
icon: () => <Icon icon="mdi:contact" width="18" height="18" />,
@ -206,6 +210,18 @@ const SidebarContent = ({
pathname: string;
updateSidebarData: (newData: boolean) => void;
}) => {
const roleLabel: Record<number, string> = {
1: "Admin",
2: "Operator",
};
const [userRoleId, setUserRoleId] = useState<number | null>(null);
useEffect(() => {
const urie = Cookies.get("urie");
setUserRoleId(urie ? Number(urie) : null);
}, []);
const { theme, toggleTheme } = useTheme();
return (
<div className="flex flex-col h-full">
@ -228,7 +244,10 @@ const SidebarContent = ({
className="flex flex-row"
>
<p className="text-lg font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-white dark:text-white">
JAECOO <span className="text-lg font-normal">Admin</span>
JAECOO{" "}
<span className="text-lg font-normal">
{userRoleId != null ? roleLabel[userRoleId] : ""}
</span>
</p>
{/* <span className="text-xs text-slate-500">Admin Panel</span> */}
</motion.div>
@ -540,7 +559,7 @@ const Sidebar = () => {
</g>
</svg>
<div className="flex flex-col gap-0.5 text-xs">
<p>admin-mabes</p>
{/* <p>admin-mabes</p> */}
<p className="underline">Logout</p>
</div>
</div>

View File

@ -47,9 +47,14 @@ import {
} from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination";
import { EditBannerDialog } from "../form/banner-edit-dialog";
import { Eye, CheckCheck } from "lucide-react";
import { Eye, CheckCheck, X } from "lucide-react";
import AgentDetailDialog from "../dialog/agent-dialog";
import { deleteAgent, getAgentData, approveAgent } from "@/service/agent";
import {
deleteAgent,
getAgentData,
approveAgent,
rejectAgent,
} from "@/service/agent";
const columns = [
{ name: "No", uid: "no" },
@ -94,12 +99,12 @@ export default function AgentTable() {
startDate: null,
endDate: null,
});
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
// 🔹 Ambil userlevelId dari cookies
// 🔹 Ambil userRoleId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // userRoleId dari cookies
setUserRoleId(urie ?? null);
}, []);
useEffect(() => {
@ -202,6 +207,40 @@ export default function AgentTable() {
initState(); // refresh table
};
const handleRejectAgent = async (id: number) => {
const MySwal = withReactContent(Swal);
const { value: message } = await MySwal.fire({
title: "Tolak Agent",
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 rejectAgent(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak agent");
close();
return;
}
close();
success("Agent berhasil ditolak");
initState(); // refresh table
};
const copyUrlArticle = async (id: number, slug: string) => {
const url =
`${window.location.protocol}//${window.location.host}` +
@ -452,7 +491,7 @@ export default function AgentTable() {
</Button>
</Link>
{/* Tombol Edit */}
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Link href={`/admin/agent/update/${item.id}`}>
<Button
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"

View File

@ -51,9 +51,12 @@ import {
getBannerData,
updateBanner,
approveBanner,
rejectBanner,
getApprovalHistory,
} from "@/service/banner";
import { Check, CheckCheck, Clock, Eye, X } from "lucide-react";
import { useRouter } from "next/navigation";
import page from "@/app/page";
const columns = [
{ name: "No", uid: "no" },
@ -96,14 +99,14 @@ export default function ArticleTable() {
startDate: null,
endDate: null,
});
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
// 🔹 Ambil userlevelId dari cookies
// 🔹 Ambil userRoleId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // userRoleId dari cookies
setUserRoleId(urie ?? null);
}, []);
useEffect(() => {
@ -259,6 +262,39 @@ export default function ArticleTable() {
initState(); // refresh table
};
const handleRejectBanner = async (id: number) => {
const { value: message } = await MySwal.fire({
title: "Tolak Banner",
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 rejectBanner(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak banner");
close();
return;
}
close();
success("Banner berhasil ditolak");
initState(); // refresh table
};
const handleReject = async () => {
if (!viewBanner) return;
@ -514,7 +550,7 @@ export default function ArticleTable() {
<Eye className="w-4 h-4 mr-1" />
Lihat
</Button>
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Button
variant="ghost"
size="sm"
@ -525,16 +561,27 @@ export default function ArticleTable() {
Edit
</Button>
)}
{/* {userLevelId === "1" && item.status_id === 1 && (
<Button
variant="ghost"
size="sm"
className="text-green-600 hover:bg-transparent hover:underline p-0"
onClick={() => handleApproveBanner(item.id)}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
{/* {userRoleId === "1" && item.status_id === 1 && (
<>
<Button
variant="ghost"
size="sm"
className="text-green-600 hover:bg-transparent hover:underline p-0"
onClick={() => handleApproveBanner(item.id)}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
<Button
variant="ghost"
size="sm"
className="text-red-600 hover:bg-transparent hover:underline p-0"
onClick={() => handleRejectBanner(item.id)}
>
<X className="w-4 h-4 mr-1" />
Reject
</Button>
</>
)} */}
<Button
variant="ghost"
@ -810,7 +857,7 @@ export default function ArticleTable() {
</div>
{/* FOOTER */}
{userLevelId !== "2" && (
{userRoleId !== "2" && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{viewBanner.status_id === 1 ? (
<>
@ -830,13 +877,13 @@ export default function ArticleTable() {
variant="destructive"
onClick={(e) => {
e.stopPropagation();
handleReject();
handleRejectBanner(viewBanner.id);
}}
>
Reject
</Button>
{userLevelId === "1" && (
{userRoleId === "1" && (
<Button
// variant="ghost"
size="sm"

View File

@ -9,6 +9,7 @@ import {
Calendar,
MapPin,
CheckCheck,
X,
} from "lucide-react";
import {
deleteGalery,
@ -16,6 +17,7 @@ import {
getGaleryData,
getGaleryFileData,
approveGalery,
rejectGalery,
} from "@/service/galery";
import { DialogDetailGaleri } from "../dialog/galery-detail-dialog";
import { DialogUpdateGaleri } from "../dialog/galery-update-dialog";
@ -23,11 +25,12 @@ import { error, success, loading, close } from "@/config/swal";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import Cookies from "js-cookie";
import { Badge } from "../ui/badge";
export default function Galery() {
const MySwal = withReactContent(Swal);
const [data, setData] = useState<any[]>([]);
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [showData, setShowData] = useState("10");
@ -80,10 +83,10 @@ export default function Galery() {
}
};
// 🔹 Ambil userlevelId dari cookies
// 🔹 Ambil userRoleId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // userRoleId dari cookies
setUserRoleId(urie ?? null);
}, []);
useEffect(() => {
@ -151,6 +154,39 @@ export default function Galery() {
fetchData(); // refresh table
};
const handleRejectGalery = async (id: number) => {
const { value: message } = await MySwal.fire({
title: "Tolak Galery",
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 rejectGalery(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak galery");
close();
return;
}
close();
success("Galery berhasil ditolak");
fetchData(); // refresh table
};
return (
<div className="mt-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
@ -175,22 +211,23 @@ export default function Galery() {
)}
{/* Status Badge */}
<span
className={`absolute top-3 left-3 text-xs px-3 py-1 rounded-full font-medium
${
<Badge
className={`absolute top-3 left-3 px-3 py-1 text-xs font-medium ${
item.status_id === 2
? "bg-green-100 text-green-700"
: item.status_id === 1
? "bg-yellow-100 text-yellow-700"
: "bg-gray-100 text-gray-600"
: item.status_id === 3
? "bg-red-100 text-red-700"
: "bg-gray-100 text-gray-600"
}`}
>
{item.status_id === 2
? "Disetujui"
: item.status_id === 1
? "Menunggu"
: "Tidak Diketahui"}
</span>
: "Ditolak"}
</Badge>
</div>
{/* Content */}
@ -218,7 +255,7 @@ export default function Galery() {
>
<Eye className="h-4 w-4" /> Lihat
</button>
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<button
onClick={() => openEdit(item.id)}
className="flex items-center gap-1 text-gray-700 hover:text-[#1F6779] transition"
@ -226,14 +263,22 @@ export default function Galery() {
<Pencil className="h-4 w-4" /> Edit
</button>
)}
{/* Tombol Approve - hanya untuk admin dan status pending */}
{/* {userLevelId === "1" && item.status_id === 1 && (
<button
className="flex items-center gap-1 text-green-600 hover:text-green-700 transition"
onClick={() => handleApproveGalery(item.id)}
>
<CheckCheck className="h-4 w-4" /> Approve
</button>
{/* Tombol Approve & Reject - hanya untuk admin dan status pending */}
{/* {userRoleId === "1" && item.status_id === 1 && (
<>
<button
className="flex items-center gap-1 text-green-600 hover:text-green-700 transition"
onClick={() => handleApproveGalery(item.id)}
>
<CheckCheck className="h-4 w-4" /> Approve
</button>
<button
className="flex items-center gap-1 text-red-600 hover:text-red-700 transition"
onClick={() => handleRejectGalery(item.id)}
>
<X className="h-4 w-4" /> Reject
</button>
</>
)} */}
<button

View File

@ -51,8 +51,9 @@ import {
deleteProduct,
getProductPagination,
approveProduct,
rejectProduct,
} from "@/service/product";
import { CheckCheck, Eye } from "lucide-react";
import { CheckCheck, Eye, X } from "lucide-react";
import { useRouter } from "next/navigation";
const columns = [
@ -96,14 +97,14 @@ export default function ProductTable() {
startDate: null,
endDate: null,
});
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
const router = useRouter();
// 🔹 Ambil userlevelId dari cookies
// 🔹 Ambil userRoleId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // userRoleId dari cookies
setUserRoleId(urie ?? null);
}, []);
useEffect(() => {
@ -213,6 +214,40 @@ export default function ProductTable() {
initState(); // refresh table
};
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 copyUrlArticle = async (id: number, slug: string) => {
const url =
`${window.location.protocol}//${window.location.host}` +
@ -476,7 +511,7 @@ export default function ProductTable() {
Lihat
</Button>
</Link>
{userLevelId !== "1" && (
{userRoleId !== "1" && (
<Link href={"/admin/product/update"}>
<Button
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"
@ -487,17 +522,28 @@ export default function ProductTable() {
</Button>
</Link>
)}
{/* {userLevelId === "1" && item.status_id === 1 && (
<Button
variant="ghost"
size="sm"
onClick={() => handleApproveProduct(item.id)}
className="text-green-600 hover:bg-transparent hover:underline p-0"
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)} */}
{userRoleId === "1" && item.status_id === 1 && (
<>
<Button
variant="ghost"
size="sm"
onClick={() => handleApproveProduct(item.id)}
className="text-green-600 hover:bg-transparent hover:underline p-0"
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => handleRejectProduct(item.id)}
className="text-red-600 hover:bg-transparent hover:underline p-0"
>
<X className="w-4 h-4 mr-1" />
Reject
</Button>
</>
)}
<Button
variant="ghost"
size="sm"

View File

@ -47,13 +47,14 @@ import {
} from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination";
import { EditBannerDialog } from "../form/banner-edit-dialog";
import { Eye, Trash2, CheckCheck } from "lucide-react";
import { Eye, Trash2, CheckCheck, X } from "lucide-react";
import AgentDetailDialog from "../dialog/agent-dialog";
import PromoDetailDialog from "../dialog/promo-dialog";
import {
deletePromotion,
getPromotionPagination,
approvePromotion,
rejectPromotion,
} from "@/service/promotion";
const columns = [
@ -100,12 +101,12 @@ export default function PromotionTable() {
startDate: null,
endDate: null,
});
const [userLevelId, setUserLevelId] = useState<string | null>(null);
const [userRoleId, setUserRoleId] = useState<string | null>(null);
// 🔹 Ambil userlevelId dari cookies
// 🔹 Ambil userRoleId dari cookies
useEffect(() => {
const ulne = Cookies.get("ulne"); // contoh: "3"
setUserLevelId(ulne ?? null);
const urie = Cookies.get("urie"); // userRoleId dari cookies
setUserRoleId(urie ?? null);
}, []);
useEffect(() => {
@ -208,6 +209,40 @@ export default function PromotionTable() {
initState(); // refresh table
};
const handleRejectPromotion = async (id: number) => {
const MySwal = withReactContent(Swal);
const { value: message } = await MySwal.fire({
title: "Tolak Promotion",
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 rejectPromotion(id, message || undefined);
if (res?.error) {
error(res.message || "Gagal menolak promotion");
close();
return;
}
close();
success("Promotion berhasil ditolak");
initState(); // refresh table
};
const copyUrlArticle = async (id: number, slug: string) => {
const url =
`${window.location.protocol}//${window.location.host}` +
@ -412,6 +447,10 @@ export default function PromotionTable() {
<span className="bg-green-100 text-green-700 text-xs px-3 py-1 rounded-full font-medium">
Disetujui
</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">
{item.status_id || "Tidak Diketahui"}
@ -436,17 +475,28 @@ export default function PromotionTable() {
</Button>
{/* Tombol Edit */}
{/* Tombol Approve - hanya untuk admin dan status pending */}
{/* {userLevelId === "1" && item.status_id === 1 && (
<Button
variant="ghost"
size="sm"
onClick={() => handleApprovePromotion(item.id)}
className="text-green-600 hover:bg-transparent hover:underline p-0"
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
{/* Tombol Approve & Reject - hanya untuk admin dan status pending */}
{/* {userRoleId === "1" && item.status_id === 1 && (
<>
<Button
variant="ghost"
size="sm"
onClick={() => handleApprovePromotion(item.id)}
className="text-green-600 hover:bg-transparent hover:underline p-0"
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => handleRejectPromotion(item.id)}
className="text-red-600 hover:bg-transparent hover:underline p-0"
>
<X className="w-4 h-4 mr-1" />
Reject
</Button>
</>
)} */}
{/* Tombol Hapus */}
<Button

View File

@ -46,3 +46,27 @@ export async function approveAgent(id: string | number) {
};
return await httpPutInterceptor(`/sales-agents/${id}/approve`, {}, headers);
}
export async function rejectAgent(id: string | number, message?: string) {
const headers = {
"content-type": "application/json",
};
return await httpPutInterceptor(
`/sales-agents/${id}/reject`,
{ message },
headers,
);
}
export async function getApprovalHistory(
moduleType: string,
moduleId: string | number,
) {
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/approval-histories/${moduleType}/${moduleId}`,
headers,
);
}

View File

@ -56,3 +56,27 @@ export async function approveBanner(id: string | number) {
};
return await httpPutInterceptor(`/banners/${id}/approve`, {}, headers);
}
export async function rejectBanner(id: string | number, message?: string) {
const headers = {
"content-type": "application/json",
};
return await httpPutInterceptor(
`/banners/${id}/reject`,
{ message },
headers,
);
}
export async function getApprovalHistory(
moduleType: string,
moduleId: string | number,
) {
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/approval-histories/${moduleType}/${moduleId}`,
headers,
);
}

View File

@ -77,3 +77,27 @@ export async function approveGalery(id: string | number) {
};
return await httpPutInterceptor(`/galleries/${id}/approve`, {}, headers);
}
export async function rejectGalery(id: string | number, message?: string) {
const headers = {
"content-type": "application/json",
};
return await httpPutInterceptor(
`/galleries/${id}/reject`,
{ message },
headers,
);
}
export async function getApprovalHistory(
moduleType: string,
moduleId: string | number,
) {
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/approval-histories/${moduleType}/${moduleId}`,
headers,
);
}

View File

@ -49,3 +49,27 @@ export async function approveProduct(id: string | number) {
};
return await httpPutInterceptor(`/products/${id}/approve`, {}, headers);
}
export async function rejectProduct(id: string | number, message?: string) {
const headers = {
"content-type": "application/json",
};
return await httpPutInterceptor(
`/products/${id}/reject`,
{ message },
headers,
);
}
export async function getApprovalHistory(
moduleType: string,
moduleId: string | number,
) {
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/approval-histories/${moduleType}/${moduleId}`,
headers,
);
}

View File

@ -44,3 +44,27 @@ export async function approvePromotion(id: string | number) {
};
return await httpPutInterceptor(`/promotions/${id}/approve`, {}, headers);
}
export async function rejectPromotion(id: string | number, message?: string) {
const headers = {
"content-type": "application/json",
};
return await httpPutInterceptor(
`/promotions/${id}/reject`,
{ message },
headers,
);
}
export async function getApprovalHistory(
moduleType: string,
moduleId: string | number,
) {
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/approval-histories/${moduleType}/${moduleId}`,
headers,
);
}