"use client"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Search, Filter, ChevronLeft, ChevronRight } from "lucide-react"; import Link from "next/link"; import { useCallback, useEffect, useMemo, useState } from "react"; import Cookies from "js-cookie"; import { formatDate } from "@/utils/global"; import { listCmsContentSubmissions } from "@/service/cms-content-submissions"; import { getArticlesForMyContent } from "@/service/article"; import { apiPayload } from "@/service/cms-landing"; import { Loader2 } from "lucide-react"; import { isApproverOrAdmin, isContributorRole, } from "@/constants/user-roles"; const PLACEHOLDER_IMG = "https://placehold.co/400x240/f1f5f9/64748b?text=Content"; type CmsRow = { id: string; domain: string; title: string; status: string; submitter_name: string; submitted_by_id: number; created_at: string; }; type ArticleRow = { id: number; title: string; thumbnailUrl?: string; isDraft?: boolean; isPublish?: boolean; publishStatus?: string; statusId?: number | null; createdAt?: string; created_at?: string; createdByName?: string; }; type UnifiedStatus = "draft" | "pending" | "approved" | "rejected"; type UnifiedItem = { key: string; source: "website" | "news"; title: string; thumb: string; status: UnifiedStatus; statusLabel: string; date: string; href: string; }; function cmsToUnified(r: CmsRow): UnifiedItem { const st = (r.status || "").toLowerCase(); let status: UnifiedStatus = "pending"; let statusLabel = "Pending Approval"; if (st === "approved") { status = "approved"; statusLabel = "Approved"; } else if (st === "rejected") { status = "rejected"; statusLabel = "Rejected"; } else if (st === "pending") { status = "pending"; statusLabel = "Pending Approval"; } return { key: `cms-${r.id}`, source: "website", title: r.title, thumb: PLACEHOLDER_IMG, status, statusLabel, date: r.created_at, href: "/admin/content-website", }; } function articleHistoryStatus(a: ArticleRow): UnifiedStatus { if (a.isDraft) return "draft"; if (a.isPublish) return "approved"; const ps = (a.publishStatus || "").toLowerCase(); if (ps.includes("reject")) return "rejected"; if (a.statusId === 3) return "rejected"; return "pending"; } function articleStatusLabel(s: UnifiedStatus): string { switch (s) { case "draft": return "Draft"; case "pending": return "Pending Approval"; case "approved": return "Approved"; case "rejected": return "Rejected"; default: return "Pending Approval"; } } function articleToUnified(r: ArticleRow): UnifiedItem { const status = articleHistoryStatus(r); const rawDate = r.createdAt ?? r.created_at ?? ""; return { key: `art-${r.id}`, source: "news", title: r.title || "Untitled", thumb: r.thumbnailUrl?.trim() || PLACEHOLDER_IMG, status, statusLabel: articleStatusLabel(status), date: rawDate, href: `/admin/news-article/detail/${r.id}`, }; } function statusBadgeClass(status: UnifiedStatus): string { switch (status) { case "pending": return "bg-yellow-100 text-yellow-800 border-yellow-200"; case "approved": return "bg-green-100 text-green-800 border-green-200"; case "rejected": return "bg-red-100 text-red-800 border-red-200"; case "draft": default: return "bg-slate-100 text-slate-700 border-slate-200"; } } const PAGE_SIZE = 8; export default function MyContent() { const [levelId, setLevelId] = useState(); const [search, setSearch] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); const [sourceFilter, setSourceFilter] = useState<"all" | "news" | "website">( "all", ); const [statusFilter, setStatusFilter] = useState< "all" | UnifiedStatus >("all"); const [loading, setLoading] = useState(true); const [cmsRows, setCmsRows] = useState([]); const [articleRows, setArticleRows] = useState([]); const [page, setPage] = useState(1); const isContributor = isContributorRole(levelId); const isApprover = isApproverOrAdmin(levelId); useEffect(() => { setLevelId(Cookies.get("urie")); }, []); useEffect(() => { const t = setTimeout(() => setDebouncedSearch(search), 350); return () => clearTimeout(t); }, [search]); const load = useCallback(async () => { if (levelId === undefined) return; setLoading(true); try { const cmsMine = isContributor; const cmsRes = await listCmsContentSubmissions({ status: "all", mine: cmsMine, page: 1, limit: 500, }); const cmsData = apiPayload(cmsRes); setCmsRows(Array.isArray(cmsData) ? cmsData : []); const artMode = isApprover ? "approver" : "own"; const artRes = await getArticlesForMyContent({ mode: artMode, page: 1, limit: 500, }); const artData = apiPayload(artRes); setArticleRows(Array.isArray(artData) ? artData : []); } finally { setLoading(false); } }, [levelId, isContributor, isApprover]); useEffect(() => { load(); }, [load]); const mergedAll = useMemo(() => { const cms = cmsRows.map(cmsToUnified); const arts = articleRows.map(articleToUnified); let list = [...cms, ...arts]; const q = search.trim().toLowerCase(); if (q) { list = list.filter( (x) => x.title.toLowerCase().includes(q) || x.statusLabel.toLowerCase().includes(q), ); } if (sourceFilter === "news") list = list.filter((x) => x.source === "news"); if (sourceFilter === "website") list = list.filter((x) => x.source === "website"); if (statusFilter !== "all") list = list.filter((x) => x.status === statusFilter); list.sort((a, b) => (a.date < b.date ? 1 : -1)); return list; }, [cmsRows, articleRows, debouncedSearch, sourceFilter, statusFilter]); const stats = useMemo(() => { const draft = mergedAll.filter((x) => x.status === "draft").length; const pending = mergedAll.filter((x) => x.status === "pending").length; const approved = mergedAll.filter((x) => x.status === "approved").length; const rejected = mergedAll.filter((x) => x.status === "rejected").length; return { total: mergedAll.length, draft, pending, approved, rejected, }; }, [mergedAll]); const totalPages = Math.max(1, Math.ceil(mergedAll.length / PAGE_SIZE)); const currentPage = Math.min(page, totalPages); const pageItems = useMemo(() => { const start = (currentPage - 1) * PAGE_SIZE; return mergedAll.slice(start, start + PAGE_SIZE); }, [mergedAll, currentPage]); useEffect(() => { setPage(1); }, [sourceFilter, statusFilter, debouncedSearch, levelId]); if (levelId === undefined) { return (
); } return (

My Content

Track all your content submissions and drafts.

{isContributor ? "Riwayat konten Anda: perubahan Content Website (setelah diajukan) dan artikel. Buka item untuk mengedit atau melanjutkan persetujuan di halaman masing-masing." : isApprover ? "Riwayat dari kontributor: Content Website dan News & Articles (tanpa draft). Persetujuan dilakukan di halaman Content Website atau detail artikel." : "Ringkasan konten terkait akun Anda."}

{[ { title: "Total Content", value: stats.total, color: "bg-blue-500" }, { title: "Drafts", value: stats.draft, color: "bg-slate-600" }, { title: "Pending", value: stats.pending, color: "bg-yellow-500" }, { title: "Approved", value: stats.approved, color: "bg-green-600" }, { title: "Revision/Rejected", value: stats.rejected, color: "bg-red-600", }, ].map((item) => (

{item.value}

{item.title}

))}
setSearch(e.target.value)} />
{loading ? (
) : pageItems.length === 0 ? (

No content found.

) : ( <>
{pageItems.map((item) => (
{item.statusLabel} {item.source === "news" ? "News & Articles" : "Content Website"}
{/* eslint-disable-next-line @next/next/no-img-element */}

{item.title}

{item.date ? formatDate(item.date) : "—"}

))}

Showing {(currentPage - 1) * PAGE_SIZE + 1} to{" "} {Math.min(currentPage * PAGE_SIZE, mergedAll.length)} of{" "} {mergedAll.length} items

Page {currentPage} / {totalPages}
)}
); }