diff --git a/components/main/category-tabs.tsx b/components/main/category-tabs.tsx index acc05a8..a1efa17 100644 --- a/components/main/category-tabs.tsx +++ b/components/main/category-tabs.tsx @@ -1,39 +1,69 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { getPublicClients, PublicClient } from "@/service/client/public-clients"; + type CategoryTabsProps = { selectedCategory: string; onCategoryChange: (category: string) => void; }; -const categories = [ - "SEMUA", - "POLRI", - "MAHKAMAH AGUNG", - "DPR", - "MPR", - "KEJAKSAAN AGUNG", - "KPK", - "PUPR", - "BSKDN", - "BUMN", - "KPU", -]; - export default function CategoryTabs({ selectedCategory, onCategoryChange, }: CategoryTabsProps) { + const [clients, setClients] = useState([]); + const [loading, setLoading] = useState(true); + + // Fetch public clients + useEffect(() => { + async function fetchClients() { + try { + const response = await getPublicClients(); + if (response?.data?.success && response.data.data) { + setClients(response.data.data); + } + } catch (error) { + console.error("Error fetching public clients:", error); + // Fallback to empty array if API fails + setClients([]); + } finally { + setLoading(false); + } + } + + fetchClients(); + }, []); + + // Create categories array with "SEMUA" first, then client names + const categories = ["SEMUA", ...clients.map(client => client.name)]; + + if (loading) { + return ( +
+ {Array.from({ length: 6 }).map((_, idx) => ( +
+ ))} +
+ ); + } + return (
{categories.map((cat, idx) => ( ))}
diff --git a/components/main/filter-publication-for-you.tsx b/components/main/filter-publication-for-you.tsx index 9afcb30..94e7ff0 100644 --- a/components/main/filter-publication-for-you.tsx +++ b/components/main/filter-publication-for-you.tsx @@ -12,15 +12,16 @@ export default function PublicationKlLayout() { const [selectedCategory, setSelectedCategory] = useState("SEMUA"); const router = useRouter(); - useEffect(() => { - const roleId = getCookiesDecrypt("urie") ?? ""; + // useEffect(() => { + // const roleId = getCookiesDecrypt("urie") ?? ""; - const allowedRoles = ["6", "7", "2", "3"]; + // const allowedRoles = ["6", "7", "2", "3"]; + + // if (!allowedRoles.includes(roleId)) { + // router.push("/auth"); + // } + // }, [router]); - if (!allowedRoles.includes(roleId)) { - router.push("/auth"); - } - }, [router]); return (
([]); + const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); - const [contentImage, setContentImage] = useState([]); const [totalPages, setTotalPages] = useState(1); - const [categoryFilter] = useState([]); - const [formatFilter] = useState([]); - const [sortBy] = useState("createdAt"); + const [totalCount, setTotalCount] = useState(0); + const [limit] = useState(12); + const MySwal = withReactContent(Swal); useEffect(() => { - getDataBookmark(); + fetchBookmarks(); }, [currentPage, selectedCategory, title, refresh]); - async function getDataBookmark() { - console.log("📡 Fetching bookmark list..."); - + const fetchBookmarks = async () => { try { - const response = await getBookmarksForUser(1, 12, "desc", "createdAt"); - console.log("✅ Bookmark response:", response); - - const bookmarks = - response?.data?.data?.content || response?.data?.data || []; - const totalPage = response?.data?.data?.totalPages || 1; - - setContentImage(bookmarks); - setTotalPages(totalPage); - } catch (error) { - console.error("❌ Gagal memuat bookmark:", error); - setContentImage([]); + setLoading(true); + const response = await getBookmarks(currentPage, limit); + + if (!response?.error) { + setBookmarks(response.data?.data || []); + setTotalPages(response.data?.meta?.totalPage || 1); + setTotalCount(response.data?.meta?.count || 0); + } else { + console.error("Failed to fetch bookmarks:", response?.error); + MySwal.fire({ + icon: "error", + title: "Error", + text: "Gagal memuat bookmark.", + confirmButtonColor: "#d33", + }); + } + } catch (err) { + console.error("Error fetching bookmarks:", err); + MySwal.fire({ + icon: "error", + title: "Error", + text: "Terjadi kesalahan saat memuat bookmark.", + confirmButtonColor: "#d33", + }); + } finally { + setLoading(false); } - } + }; + + const handleDeleteBookmark = async (bookmarkId: number, articleId: number, articleTitle: string) => { + const result = await MySwal.fire({ + title: "Hapus Bookmark", + text: `Apakah Anda yakin ingin menghapus "${articleTitle}" dari bookmark?`, + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#d33", + cancelButtonColor: "#3085d6", + confirmButtonText: "Ya, Hapus!", + cancelButtonText: "Batal", + }); + + if (result.isConfirmed) { + try { + const response = await toggleBookmarkById(articleId); + + if (response?.data?.success || !response?.error) { + // Remove from local state + setBookmarks(prev => prev.filter(bookmark => bookmark.id !== bookmarkId)); + setTotalCount(prev => prev - 1); + + MySwal.fire({ + icon: "success", + title: "Berhasil", + text: "Bookmark berhasil dihapus.", + timer: 1500, + showConfirmButton: false, + }); + } else { + MySwal.fire({ + icon: "error", + title: "Gagal", + text: "Gagal menghapus bookmark.", + confirmButtonColor: "#d33", + }); + } + } catch (err) { + console.error("Error deleting bookmark:", err); + MySwal.fire({ + icon: "error", + title: "Error", + text: "Terjadi kesalahan saat menghapus bookmark.", + confirmButtonColor: "#d33", + }); + } + } + }; const goToPage = (page: number) => { setCurrentPage(page); }; + if (loading) { + return ( +
+ {Array.from({ length: 6 }).map((_, index) => ( +
+
+
+
+
+
+
+
+
+ ))} +
+ ); + } + + if (bookmarks.length === 0) { + return ( +
+
📚
+

Tidak Ada Bookmark

+

Anda belum menyimpan artikel apapun ke bookmark.

+
+ ); + } + return (
+ {/* Header */} +
+

Bookmark Saya

+

Total {totalCount} artikel tersimpan

+
+ {/* Grid Card */}
- {contentImage.length === 0 ? ( -
- Tidak ada artikel tersimpan. + {bookmarks.map((bookmark) => ( +
+
+ + {bookmark.article?.title + +
+
+
+ + {getContentTypeLabel(bookmark.article?.typeId || 0)} + + {/* + Bookmark + */} +
+

+ Disimpan: {formatTanggal(bookmark.createdAt)} +

+ +

+ {bookmark.article?.title} +

+ + +
+
+ + +
+ +
+
- ) : ( - contentImage.map((item: any, idx: number) => { - const article = item.article || item; // jaga-jaga kalau API return nested - return ( - c.title).join(", ") || - article.tags || - "-" - } - date={new Date(article.createdAt).toLocaleString("id-ID", { - day: "2-digit", - month: "short", - year: "numeric", - hour: "2-digit", - minute: "2-digit", - timeZoneName: "short", - })} - title={article.title} - description={article.description || ""} - /> - ); - }) - )} + ))}
{/* Pagination */} diff --git a/hooks/use-auth.ts b/hooks/use-auth.ts index 2607bd8..04e55c2 100644 --- a/hooks/use-auth.ts +++ b/hooks/use-auth.ts @@ -177,7 +177,11 @@ export const useAuth = (): AuthContextType => { // Reset rate limiter on successful login loginRateLimiter.resetAttempts(credentials.username); - router.push("/admin/dashboard"); + if (profile?.userRoleId === 4 || profile?.userRoleId === 5) { + router.push("/"); + } else { + router.push("/admin/dashboard"); + } } catch (error: any) { const errorMessage = error?.message || "Login failed"; setState((prev) => ({ diff --git a/service/content.ts b/service/content.ts index bb87f81..736a62f 100644 --- a/service/content.ts +++ b/service/content.ts @@ -417,4 +417,64 @@ export async function getBookmarkSummaryForUser() { export async function deleteBookmark(id: string | number) { const url = `/bookmarks/${id}`; return await httpDeleteInterceptor(url); +} + +export async function toggleBookmarkById(articleId: string | number) { + const url = `/bookmarks/toggle/${articleId}`; + return await httpPostInterceptor(url); +} + +// Bookmarks API +export interface BookmarkItem { + id: number; + userId: number; + articleId: number; + isActive: boolean; + createdAt: string; + updatedAt: string; + article: { + id: number; + title: string; + slug: string; + description: string; + htmlDescription: string; + categoryId: number; + typeId: number; + tags: string; + thumbnailUrl: string; + pageUrl: string | null; + createdById: number; + shareCount: number; + viewCount: number; + commentCount: number; + statusId: number; + isBanner: boolean; + isPublish: boolean; + publishedAt: string | null; + isActive: boolean; + createdAt: string; + updatedAt: string; + }; +} + +export interface BookmarksResponse { + success: boolean; + code: number; + messages: string[]; + data: BookmarkItem[]; + meta: { + limit: number; + page: number; + nextPage: number; + previousPage: number; + count: number; + totalPage: number; + sort: string; + sortBy: string; + }; +} + +export async function getBookmarks(page = 1, limit = 10) { + const url = `bookmarks?page=${page}&limit=${limit}`; + return httpGetInterceptor(url); } \ No newline at end of file