jaecoo-kelapagading/components/table/article-table.tsx

1042 lines
35 KiB
TypeScript
Raw Normal View History

2025-07-14 07:31:51 +00:00
"use client";
import {
BannerIcon,
CopyIcon,
CreateIconIon,
DeleteIcon,
DotsYIcon,
EyeIconMdi,
SearchIcon,
} from "@/components/icons";
import { close, error, loading, success, successToast } from "@/config/swal";
import { Article } from "@/types/globals";
import { convertDateFormat } from "@/utils/global";
import Link from "next/link";
import { Key, useCallback, useEffect, useState } from "react";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import Cookies from "js-cookie";
import {
deleteArticle,
getArticleByCategory,
updateIsBannerArticle,
} from "@/service/article";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from "@/components/ui/select";
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination";
2025-11-18 06:56:39 +00:00
import { EditBannerDialog } from "../form/banner-edit-dialog";
import {
deleteBanner,
getBannerData,
updateBanner,
approveBanner,
} from "@/service/banner";
2026-01-19 16:39:33 +00:00
import { Check, CheckCheck, Clock, Eye, X } from "lucide-react";
2026-01-19 11:25:14 +00:00
import { useRouter } from "next/navigation";
2025-07-14 07:31:51 +00:00
const columns = [
{ name: "No", uid: "no" },
{ name: "Judul", uid: "title" },
{ name: "Banner", uid: "isBanner" },
{ name: "Kategori", uid: "category" },
{ name: "Tanggal Unggah", uid: "createdAt" },
{ name: "Kreator", uid: "createdByName" },
{ name: "Status", uid: "isPublish" },
{ name: "Aksi", uid: "actions" },
];
const columnsOtherRole = [
{ name: "No", uid: "no" },
{ name: "Judul", uid: "title" },
{ name: "Kategori", uid: "category" },
{ name: "Tanggal Unggah", uid: "createdAt" },
{ name: "Kreator", uid: "createdByName" },
{ name: "Status", uid: "isPublish" },
{ name: "Aksi", uid: "actions" },
];
// interface Category {
// id: number;
// title: string;
// }
export default function ArticleTable() {
const MySwal = withReactContent(Swal);
const username = Cookies.get("username");
const userId = Cookies.get("uie");
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [article, setArticle] = useState<any[]>([]);
const [showData, setShowData] = useState("10");
const [search, setSearch] = useState("");
const [categories, setCategories] = useState<any>([]);
const [selectedCategories, setSelectedCategories] = useState<any>("");
const [startDateValue, setStartDateValue] = useState({
startDate: null,
endDate: null,
});
2026-01-19 11:25:14 +00:00
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);
}, []);
2025-07-14 07:31:51 +00:00
useEffect(() => {
initState();
getCategories();
}, []);
async function getCategories() {
const res = await getArticleByCategory();
const data = res?.data?.data;
setCategories(data);
}
const initState = useCallback(async () => {
loading();
const req = {
limit: showData,
page: page,
search: search,
};
2025-11-18 06:56:39 +00:00
const res = await getBannerData(req);
2025-07-14 07:31:51 +00:00
await getTableNumber(parseInt(showData), res.data?.data);
setTotalPage(res?.data?.meta?.totalPage);
close();
}, [page]);
const getTableNumber = async (limit: number, data: Article[]) => {
if (data) {
const startIndex = limit * (page - 1);
let iterate = 0;
const newData = data.map((value: any) => {
iterate++;
value.no = startIndex + iterate;
return value;
});
setArticle(newData);
} else {
setArticle([]);
}
};
async function doDelete(id: any) {
// loading();
2025-11-18 06:56:39 +00:00
const resDelete = await deleteBanner(id);
2025-07-14 07:31:51 +00:00
if (resDelete?.error) {
error(resDelete.message);
return false;
}
close();
success("Berhasil Hapus");
initState();
}
const handleDelete = (id: any) => {
MySwal.fire({
title: "Hapus Data",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
const handleBanner = async (id: number, status: boolean) => {
const res = await updateIsBannerArticle(id, status);
if (res?.error) {
error(res?.message);
return false;
}
initState();
};
2025-11-18 06:56:39 +00:00
const [openEditDialog, setOpenEditDialog] = useState(false);
const [selectedBanner, setSelectedBanner] = useState<any>(null);
const [openPreview, setOpenPreview] = useState(false);
2026-01-18 17:01:09 +00:00
const [openViewDialog, setOpenViewDialog] = useState(false);
const [viewBanner, setViewBanner] = useState<any>(null);
const [openApproverHistory, setOpenApproverHistory] = useState(false);
2026-01-19 11:25:14 +00:00
const [openCommentModal, setOpenCommentModal] = useState(false);
const [commentValue, setCommentValue] = useState("");
const handleSubmitComment = async () => {
// await api.post("/banner/comment", {
// bannerId: viewBanner.id,
// comment: commentValue,
// });
setOpenCommentModal(false);
};
2026-01-18 17:01:09 +00:00
const handleView = (item: any) => {
setViewBanner(item);
setOpenViewDialog(true);
};
2025-11-18 06:56:39 +00:00
const [previewImage, setPreviewImage] = useState<string | null>(null);
2026-01-18 17:01:09 +00:00
const handleOpenApproverHistory = () => {
setOpenApproverHistory(true);
};
2025-11-18 06:56:39 +00:00
const handleEdit = (item: any) => {
setSelectedBanner({
id: item.id,
title: item.title,
thumbnail_url: item.thumbnail_url, // FIX
position: item.position, // FIX
});
setOpenEditDialog(true);
};
const handleUpdateBanner = async (formData: FormData, id: number) => {
await updateBanner(formData, id);
};
// const handleApprove = async () => {
// if (!viewBanner) return;
2026-01-19 16:39:33 +00:00
// loading();
// const formData = new FormData();
// formData.append("status", "2"); // APPROVED
2026-01-19 16:39:33 +00:00
// const res = await updateBanner(formData, viewBanner.id);
2026-01-19 16:39:33 +00:00
// if (res?.error) {
// error(res.message);
// return;
// }
2026-01-19 16:39:33 +00:00
// close();
// success("Banner berhasil disetujui");
// setOpenViewDialog(false);
// initState(); // refresh table
// };
2026-01-19 16:39:33 +00:00
2026-01-20 01:04:42 +00:00
const handleApproveBanner = async (id: number) => {
loading();
const res = await approveBanner(id);
if (res?.error) {
error(res.message || "Gagal menyetujui banner");
close();
return;
}
close();
success("Banner berhasil disetujui");
initState(); // refresh table
};
2026-01-19 16:39:33 +00:00
const handleReject = async () => {
if (!viewBanner) return;
loading();
const formData = new FormData();
formData.append("status", "3"); // REJECTED
const res = await updateBanner(formData, viewBanner.id);
if (res?.error) {
error(res.message);
return;
}
close();
success("Banner berhasil ditolak");
setOpenViewDialog(false);
initState(); // refresh table
};
2025-11-18 06:56:39 +00:00
const handlePreview = (imgUrl: string) => {
setPreviewImage(imgUrl);
setOpenPreview(true);
};
2025-07-14 07:31:51 +00:00
const copyUrlArticle = async (id: number, slug: string) => {
const url =
`${window.location.protocol}//${window.location.host}` +
"/news/detail/" +
`${id}-${slug}`;
try {
await navigator.clipboard.writeText(url);
successToast("Success", "Article Copy to Clipboard");
setTimeout(() => {}, 1500);
} catch (err) {
("Failed to copy!");
}
};
const renderCell = useCallback(
(article: any, columnKey: Key) => {
const cellValue = article[columnKey as keyof any];
switch (columnKey) {
case "isPublish":
2026-01-18 17:01:09 +00:00
return <p>{article.isPublish ? "Publish" : "Draft"}</p>;
2025-07-14 07:31:51 +00:00
case "isBanner":
return <p>{article.isBanner ? "Ya" : "Tidak"}</p>;
case "createdAt":
return <p>{convertDateFormat(article.createdAt)}</p>;
case "category":
return (
<p>
{article?.categories?.map((list: any) => list.title).join(", ") +
" "}
</p>
);
case "actions":
return (
<div className="relative flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<DotsYIcon className="h-5 w-5 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuItem
onClick={() => copyUrlArticle(article.id, article.slug)}
>
<CopyIcon className="mr-2 h-4 w-4" />
Copy Url Article
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href={`/admin/article/detail/${article.id}`}
className="flex items-center"
>
<EyeIconMdi className="mr-2 h-4 w-4" />
Detail
</Link>
</DropdownMenuItem>
{(username === "admin-mabes" ||
Number(userId) === article.createdById) && (
<DropdownMenuItem asChild>
<Link
href={`/admin/article/edit/${article.id}`}
className="flex items-center"
>
<CreateIconIon className="mr-2 h-4 w-4" />
Edit
</Link>
</DropdownMenuItem>
)}
{username === "admin-mabes" && (
<DropdownMenuItem
onClick={() =>
handleBanner(article.id, !article.isBanner)
}
>
<BannerIcon className="mr-2 h-4 w-4" />
{article.isBanner
? "Hapus dari Banner"
: "Jadikan Banner"}
</DropdownMenuItem>
)}
{(username === "admin-mabes" ||
Number(userId) === article.createdById) && (
<DropdownMenuItem onClick={() => handleDelete(article.id)}>
<DeleteIcon className="mr-2 h-4 w-4 text-red-500" />
Delete
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
default:
return cellValue;
}
},
2026-01-19 11:25:14 +00:00
[article, page],
2025-07-14 07:31:51 +00:00
);
let typingTimer: NodeJS.Timeout;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
setPage(1);
initState();
}
return (
<>
<div className="py-3">
2025-11-18 06:56:39 +00:00
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
{/* Header */}
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
Daftar Banner
</div>
{/* Table */}
<Table className="w-full text-sm">
<TableHeader>
<TableRow className="bg-[#BCD4DF] text-[#008080]">
<TableHead className="w-[40px] text-[#008080]">NO</TableHead>
<TableHead className="text-[#008080]">JUDUL / NAMA</TableHead>
<TableHead className="text-[#008080] text-center">
PREVIEW KONTEN
</TableHead>
<TableHead className="text-[#008080] text-center w-[100px]">
URUTAN
</TableHead>
<TableHead className="text-[#008080] text-center w-[120px]">
STATUS
</TableHead>
<TableHead className="text-[#008080] text-center w-[120px]">
AKSI
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{article.length > 0 ? (
article.map((item, index) => (
<TableRow
key={item.id}
className="border-b hover:bg-gray-50 transition-colors"
>
<TableCell className="text-gray-700">{index + 1}</TableCell>
{/* JUDUL */}
<TableCell className="font-medium text-gray-900">
<p className="font-semibold">{item.title}</p>
<p className="text-gray-500 text-sm">
{item.subtitle ?? ""}
</p>
</TableCell>
{/* PREVIEW */}
<TableCell className="flex justify-center">
<div
className="w-[80px] h-[80px] overflow-hidden rounded-md border bg-gray-100 cursor-pointer hover:opacity-80 transition"
onClick={() =>
item.thumbnail_url &&
handlePreview(item.thumbnail_url)
}
>
{item.thumbnail_url ? (
<img
src={item.thumbnail_url}
alt={item.title}
className="w-full h-full object-cover"
/>
) : (
<div className="flex items-center justify-center h-full text-gray-400 text-xs">
No Image
</div>
)}
</div>
</TableCell>
{/* URUTAN */}
<TableCell className="text-center font-medium text-gray-700">
{item.position}
</TableCell>
{/* STATUS */}
<TableCell className="text-center">
2026-01-20 01:04:42 +00:00
{item.status_id === 1 ? (
2026-01-19 16:39:33 +00:00
<span className="bg-yellow-100 text-yellow-700 text-xs px-3 py-1 rounded-full font-medium">
Menunggu
</span>
2026-01-20 01:04:42 +00:00
) : item.status_id === 2 ? (
2025-11-18 06:56:39 +00:00
<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>
2026-01-19 16:39:33 +00:00
) : (
<span className="bg-gray-100 text-gray-600 text-xs px-3 py-1 rounded-full font-medium">
2026-01-20 01:04:42 +00:00
{item.status_id || "Tidak Diketahui"}
2026-01-19 16:39:33 +00:00
</span>
)}
2025-11-18 06:56:39 +00:00
</TableCell>
{/* AKSI */}
<TableCell className="text-center">
<div className="flex justify-center gap-3">
<Button
variant="ghost"
size="sm"
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"
2026-01-18 17:01:09 +00:00
onClick={() => handleView(item)}
2025-11-18 06:56:39 +00:00
>
2026-01-18 17:01:09 +00:00
<Eye className="w-4 h-4 mr-1" />
Lihat
2025-11-18 06:56:39 +00:00
</Button>
{userLevelId !== "1" && (
2026-01-19 11:25:14 +00:00
<Button
variant="ghost"
size="sm"
className="text-[#0F6C75] hover:bg-transparent hover:underline p-0"
onClick={() => handleEdit(item)}
>
<CreateIconIon className="w-4 h-4 mr-1" />
Edit
</Button>
)}
{/* {userLevelId === "1" && item.status_id === 1 && (
2026-01-20 01:04:42 +00:00
<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>
)} */}
2025-11-18 06:56:39 +00:00
<Button
variant="ghost"
size="sm"
className="text-red-600 hover:bg-transparent hover:underline p-0"
onClick={() => handleDelete(item.id)}
>
<DeleteIcon className="w-4 h-4 mr-1 text-red-600" />
Hapus
</Button>
</div>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={6}
className="text-center py-6 text-gray-500"
>
Tidak ada data untuk ditampilkan.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
{/* Footer Pagination */}
<div className="flex items-center justify-between px-6 py-3 border-t text-sm text-gray-600">
<p>
Menampilkan {article.length} dari {article.length} data
</p>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
className="rounded-full px-3"
disabled={page === 1}
onClick={() => setPage(page - 1)}
2025-07-14 07:31:51 +00:00
>
2025-11-18 06:56:39 +00:00
Previous
</Button>
<p>
Halaman {page} dari {totalPage}
</p>
<Button
variant="outline"
size="sm"
className="rounded-full px-3"
disabled={page === totalPage}
onClick={() => setPage(page + 1)}
>
Next
</Button>
2025-07-14 07:31:51 +00:00
</div>
2025-11-18 06:56:39 +00:00
</div>
</div>
</div>
<EditBannerDialog
open={openEditDialog}
onOpenChange={setOpenEditDialog}
bannerData={selectedBanner}
onSubmit={handleUpdateBanner}
/>
{/* Preview Dialog */}
{openPreview && (
<div
className="fixed inset-0 flex items-center justify-center bg-black/50 z-50 p-4"
onClick={() => setOpenPreview(false)}
>
<div
className="bg-white rounded-xl overflow-hidden shadow-2xl max-w-md w-full relative"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */}
<div className="bg-[#0F6C75] text-white px-5 py-4 flex flex-col gap-1 relative">
{/* Tombol close */}
<button
onClick={() => setOpenPreview(false)}
className="absolute top-3 right-4 text-white/80 hover:text-white text-lg"
2025-07-14 07:31:51 +00:00
>
2025-11-18 06:56:39 +00:00
</button>
2026-01-18 17:01:09 +00:00
<h2 className="text-lg font-semibold">JAEC00 J7 SHS-P</h2>
2025-11-18 06:56:39 +00:00
<p className="text-sm text-white/90">DELICATE OFF-ROAD SUV</p>
{/* Status badge */}
<div className="flex items-center gap-2 mt-1">
<span className="bg-yellow-100 text-yellow-800 text-xs font-medium px-3 py-1 rounded-full">
Menunggu
</span>
<span className="bg-white/20 text-white text-xs px-2 py-[1px] rounded-full">
1
</span>
</div>
2025-07-14 07:31:51 +00:00
</div>
2025-11-18 06:56:39 +00:00
{/* IMAGE PREVIEW */}
<div className="bg-[#f8fafc] p-4 flex justify-center items-center">
<img
src={previewImage ?? ""}
alt="Preview"
className="rounded-lg w-full h-auto object-contain"
2025-07-14 07:31:51 +00:00
/>
</div>
2025-11-18 06:56:39 +00:00
{/* FOOTER */}
<div className="border-t text-center py-3 bg-[#E3EFF4]">
<button
onClick={() => setOpenPreview(false)}
className="text-[#0F6C75] font-medium hover:underline"
>
Tutup
</button>
</div>
2025-07-14 07:31:51 +00:00
</div>
</div>
2025-11-18 06:56:39 +00:00
)}
2026-01-18 17:01:09 +00:00
{openViewDialog && viewBanner && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenViewDialog(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-xl 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={() => setOpenViewDialog(false)}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Detail Banner</h2>
{/* Badge */}
<div className="flex items-center gap-2 mt-3">
<span
2026-01-19 16:39:33 +00:00
className={`text-xs font-medium px-3 py-1 rounded-full ${
viewBanner.status_id === 1
2026-01-19 16:39:33 +00:00
? "bg-yellow-100 text-yellow-800"
: viewBanner.status_id === 2
2026-01-19 16:39:33 +00:00
? "bg-green-100 text-green-800"
: viewBanner.status_id === 3
2026-01-19 16:39:33 +00:00
? "bg-red-100 text-red-800"
: "bg-gray-100 text-gray-800"
}`}
2026-01-18 17:01:09 +00:00
>
{viewBanner.status_id === 1
2026-01-19 16:39:33 +00:00
? "Menunggu"
: viewBanner.status_id === 2
2026-01-19 16:39:33 +00:00
? "Disetujui"
: viewBanner.status_id === 3
? "Ditolak"
2026-01-19 16:39:33 +00:00
: "Tidak Diketahui"}
2026-01-18 17:01:09 +00:00
</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">
{viewBanner.position}
</span>
</div>
</div>
{/* BODY */}
<div className="p-6 space-y-6">
{/* JUDUL */}
<div>
<label className="block text-sm font-medium text-gray-500 mb-2">
Judul Banner <span className="text-red-500">*</span>
</label>
<div className="border rounded-lg p-3 text-gray-800 bg-gray-50 whitespace-pre-line">
{viewBanner.title}
</div>
</div>
{/* IMAGE */}
<div>
<label className="block text-sm font-medium text-gray-500 mb-2">
Upload File <span className="text-red-500">*</span>
</label>
<div className="w-[140px] h-[140px] rounded-lg overflow-hidden border bg-gray-100">
{viewBanner.thumbnail_url ? (
<img
src={viewBanner.thumbnail_url}
alt={viewBanner.title}
className="w-full h-full object-cover"
/>
) : (
<div className="flex items-center justify-center h-full text-gray-400 text-xs">
No Image
</div>
)}
</div>
</div>
{/* TIMELINE */}
<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">
2026-01-19 16:39:33 +00:00
<Check className="w-4 h-4 text-green-600" />
2026-01-18 17:01:09 +00:00
</div>
<div>
<p className="font-medium text-gray-800">
2026-01-19 16:39:33 +00:00
Diupload oleh Operator
2026-01-18 17:01:09 +00:00
</p>
<p className="text-sm text-gray-500">
{convertDateFormat(viewBanner.created_at)} WIB
</p>
</div>
</div>
<div className="flex gap-3">
2026-01-19 16:39:33 +00:00
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
viewBanner.status_id === 1
2026-01-19 16:39:33 +00:00
? "bg-yellow-100"
: viewBanner.status_id === 2
2026-01-19 16:39:33 +00:00
? "bg-green-100"
: "bg-red-100"
}`}
>
{viewBanner.status_id === 1 ? (
2026-01-19 16:39:33 +00:00
<Clock className="w-4 h-4 text-yellow-700" />
) : viewBanner.status_id === 2 ? (
2026-01-19 16:39:33 +00:00
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-red-700" />
)}
2026-01-18 17:01:09 +00:00
</div>
2026-01-19 16:39:33 +00:00
2026-01-18 17:01:09 +00:00
<div>
<p className="font-medium text-gray-800">
{viewBanner.status_id === 1
2026-01-19 16:39:33 +00:00
? "Menunggu disetujui oleh Approver"
: viewBanner.status_id === 2
2026-01-19 16:39:33 +00:00
? "Disetujui oleh Approver"
: "Ditolak oleh Approver"}
2026-01-18 17:01:09 +00:00
</p>
2026-01-19 16:39:33 +00:00
2026-01-18 17:01:09 +00:00
<p className="text-sm text-gray-500">
{convertDateFormat(viewBanner.updated_at)} WIB
</p>
</div>
</div>
2026-01-19 16:39:33 +00:00
<div className="border rounded-lg px-3 py-3">
<p>Comment : </p>
<div className="flex flex-row justify-between">
<button
onClick={handleOpenApproverHistory}
className="text-sm text-blue-600 hover:underline mt-2"
>
View Approver History
</button>
<p>Jaecoo - Approver | 10/11/2026</p>
</div>
</div>
2026-01-18 17:01:09 +00:00
</div>
</div>
</div>
{/* FOOTER */}
2026-01-19 11:25:14 +00:00
{userLevelId !== "2" && (
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
{viewBanner.status_id === 1 ? (
2026-01-19 16:39:33 +00:00
<>
<Button
variant="secondary"
className="bg-blue-200 hover:bg-blue-400"
onClick={(e) => {
e.stopPropagation();
setOpenCommentModal(true);
}}
>
Beri Tanggapan
</Button>
<Button
className=" w-[180]"
2026-01-19 16:39:33 +00:00
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();
handleApproveBanner(viewBanner.id);
}}
>
<CheckCheck className="w-4 h-4 mr-1" />
Approve
</Button>
)}
2026-01-19 16:39:33 +00:00
</>
) : (
<Button
variant="secondary"
className="mx-auto"
onClick={(e) => {
e.stopPropagation();
setOpenViewDialog(false);
}}
>
Tutup
</Button>
)}
2026-01-18 17:01:09 +00:00
</div>
2026-01-19 11:25:14 +00:00
)}
2026-01-18 17:01:09 +00:00
</div>
</div>
)}
{openApproverHistory && (
<div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenApproverHistory(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-3xl 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={() => setOpenApproverHistory(false)}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Approver History</h2>
<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">
1
</span>
</div>
</div>
{/* BODY */}
<div className="p-6 grid grid-cols-[1fr_auto_1fr] gap-6 items-start">
{/* LEFT TIMELINE */}
<div className="relative space-y-6">
{/* Upload */}
<div className="flex flex-col items-center">
<span className="bg-[#C7DDE4] text-[#0F6C75] text-xs px-4 py-1 rounded-full">
Upload
</span>
<div className="w-px h-6 bg-gray-300" />
</div>
{/* Diterima */}
<div className="relative bg-[#1F6779] text-white rounded-xl p-4">
<h4 className="font-semibold text-sm mb-2">Diterima</h4>
<span className="inline-block bg-[#E3EFF4] text-[#0F6C75] text-xs px-3 py-1 rounded-full">
Direview oleh: approver-jaecoo1
</span>
</div>
<div className="w-px h-6 bg-gray-300 mx-auto" />
{/* Pending */}
<div className="relative bg-[#B36A00] text-white rounded-xl p-4">
<h4 className="font-semibold text-sm mb-2">Pending</h4>
<span className="inline-block bg-[#FFF6CC] text-[#7A4A00] text-xs px-3 py-1 rounded-full">
Direview oleh: approver-jaecoo1
</span>
</div>
</div>
{/* ARROW */}
<div className="flex flex-col gap-20 text-gray-500 font-bold">
<span>&gt;</span>
<span>&gt;</span>
</div>
{/* RIGHT NOTES */}
<div className="space-y-14">
<div>
<div className="bg-[#C7DDE4] text-sm px-4 py-2 rounded-lg">
Catatan:
</div>
</div>
<div>
<div className="bg-[#FFF9C4] text-sm px-4 py-2 rounded-lg">
Catatan:
</div>
</div>
</div>
</div>
{/* FOOTER */}
<div className="border-t bg-[#F2F7FA] text-center py-3">
<button
onClick={() => setOpenApproverHistory(false)}
className="text-[#0F6C75] font-medium hover:underline"
>
Tutup
</button>
</div>
</div>
</div>
)}
2026-01-19 11:25:14 +00:00
{openCommentModal && viewBanner && (
<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">
{viewBanner.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>
)}
2025-07-14 07:31:51 +00:00
</>
);
}