209 lines
6.3 KiB
TypeScript
209 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import Image from "next/image";
|
|
import { Eye, Pencil, Trash2, Calendar, MapPin } from "lucide-react";
|
|
import { deleteGalery, getGaleryById, getGaleryData } from "@/service/galery";
|
|
import { DialogDetailGaleri } from "../dialog/galery-detail-dialog";
|
|
import { DialogUpdateGaleri } from "../dialog/galery-update-dialog";
|
|
import { error, success } from "@/config/swal";
|
|
import withReactContent from "sweetalert2-react-content";
|
|
import Swal from "sweetalert2";
|
|
|
|
export default function Galery() {
|
|
const MySwal = withReactContent(Swal);
|
|
const [data, setData] = useState([]);
|
|
const [page, setPage] = useState(1);
|
|
const [showData, setShowData] = useState("10");
|
|
const [search, setSearch] = useState("");
|
|
const [showDetail, setShowDetail] = useState(false);
|
|
const [detailData, setDetailData] = useState<any>(null);
|
|
const [showEdit, setShowEdit] = useState(false);
|
|
const [editData, setEditData] = useState<any>(null);
|
|
|
|
const fetchData = async () => {
|
|
try {
|
|
const req = {
|
|
limit: showData,
|
|
page: page,
|
|
search: search,
|
|
};
|
|
|
|
const res = await getGaleryData(req);
|
|
|
|
// Pastikan respons API sesuai bentuknya
|
|
// Misal: { data: [...], total: number }
|
|
setData(res?.data?.data);
|
|
} catch (error) {
|
|
console.error("Error fetch galeri:", error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [page, showData, search]);
|
|
|
|
const openDetail = async (id: number) => {
|
|
try {
|
|
const res = await getGaleryById(id);
|
|
setDetailData(res?.data?.data);
|
|
setShowDetail(true);
|
|
} catch (err) {
|
|
console.error("Error get detail:", err);
|
|
}
|
|
};
|
|
|
|
const openEdit = async (id: number) => {
|
|
try {
|
|
const res = await getGaleryById(id);
|
|
setEditData(res?.data?.data);
|
|
setShowEdit(true);
|
|
} catch (error) {
|
|
console.error("Error open edit:", error);
|
|
}
|
|
};
|
|
|
|
async function doDelete(id: any) {
|
|
// loading();
|
|
const resDelete = await deleteGalery(id);
|
|
|
|
if (resDelete?.error) {
|
|
error(resDelete.message);
|
|
return false;
|
|
}
|
|
close();
|
|
success("Berhasil Hapus");
|
|
fetchData();
|
|
}
|
|
|
|
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);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="mt-6">
|
|
{/* Card Wrapper */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
{data?.map((item: any) => (
|
|
<div
|
|
key={item.id}
|
|
className="bg-white shadow-md rounded-2xl overflow-hidden border"
|
|
>
|
|
{/* Image */}
|
|
<div className="relative w-full h-48">
|
|
<Image
|
|
src={item.image_url}
|
|
alt={item.title}
|
|
fill
|
|
className="object-cover"
|
|
/>
|
|
|
|
{/* Status Badge */}
|
|
<span
|
|
className={`absolute top-3 left-3 text-xs px-3 py-1 rounded-full font-medium
|
|
${
|
|
item.is_active
|
|
? "bg-green-100 text-green-700"
|
|
: "bg-red-100 text-red-600"
|
|
}`}
|
|
>
|
|
{item.is_active ? "Aktif" : "Tidak Aktif"}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-4 space-y-3">
|
|
<h2 className="text-[#1F6779] text-lg font-semibold">
|
|
{item.title}
|
|
</h2>
|
|
<p className="text-gray-600 text-sm">{item.desc ?? "-"}</p>
|
|
|
|
{/* Date */}
|
|
<div className="flex items-center gap-2 text-sm text-gray-700">
|
|
<Calendar className="h-4 w-4" />{" "}
|
|
{new Date(item.created_at).toLocaleDateString("id-ID")}
|
|
</div>
|
|
{/* Location (jika tidak ada, tampilkan strip) */}
|
|
<div className="flex items-center gap-2 text-sm text-gray-700">
|
|
<MapPin className="h-4 w-4" /> {item.location ?? "-"}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer Actions */}
|
|
<div className="border-t px-4 py-3 flex items-center justify-between text-sm">
|
|
<button
|
|
onClick={() => openDetail(item.id)}
|
|
className="flex items-center gap-1 text-gray-700 hover:text-[#1F6779] transition"
|
|
>
|
|
<Eye className="h-4 w-4" /> Lihat
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => openEdit(item.id)}
|
|
className="flex items-center gap-1 text-gray-700 hover:text-[#1F6779] transition"
|
|
>
|
|
<Pencil className="h-4 w-4" /> Edit
|
|
</button>
|
|
|
|
<button
|
|
className="flex items-center gap-1 text-red-600 hover:text-red-700 transition"
|
|
onClick={() => handleDelete(item.id)}
|
|
>
|
|
<Trash2 className="h-4 w-4" /> Hapus
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Pagination */}
|
|
<div className="flex items-center justify-between mt-6 text-sm">
|
|
<p>Menampilkan {data.length} data</p>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
className="px-3 py-1 border rounded-md bg-gray-100 text-gray-700 disabled:opacity-50"
|
|
disabled={page === 1}
|
|
onClick={() => setPage((prev) => prev - 1)}
|
|
>
|
|
Previous
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-1 border rounded-md bg-gray-100 text-gray-700"
|
|
onClick={() => setPage((prev) => prev + 1)}
|
|
>
|
|
Next
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{showDetail && detailData && (
|
|
<DialogDetailGaleri
|
|
open={showDetail}
|
|
onClose={() => setShowDetail(false)}
|
|
data={detailData}
|
|
/>
|
|
)}
|
|
{showEdit && editData && (
|
|
<DialogUpdateGaleri
|
|
open={showEdit}
|
|
onClose={() => setShowEdit(false)}
|
|
data={editData}
|
|
onUpdated={fetchData} // refresh setelah update
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|