update form article
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Anang Yusman 2026-02-26 12:11:35 +08:00
parent 4d45e89a28
commit cf78d137ed
2 changed files with 111 additions and 121 deletions

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@ -15,74 +15,54 @@ import {
} from "@/components/ui/table"; } from "@/components/ui/table";
import { Search, Filter, Eye, Pencil, Trash2, Plus } from "lucide-react"; import { Search, Filter, Eye, Pencil, Trash2, Plus } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { getArticlePagination } from "@/service/article";
import { formatDate } from "@/utils/global";
import { close, loading } from "@/config/swal";
export default function NewsImage() { export default function NewsImage() {
const [activeCategory, setActiveCategory] = useState("All"); const [articles, setArticles] = useState<any[]>([]);
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [search, setSearch] = useState("");
const categories = [ useEffect(() => {
{ name: "All", count: 24 }, fetchData();
{ name: "Technology", count: 8 }, }, [page, search]);
{ name: "Partnership", count: 5 },
{ name: "Investment", count: 3 },
{ name: "News", count: 4 },
{ name: "Event", count: 2 },
{ name: "CSR", count: 2 },
];
const articles = [ async function fetchData() {
{ loading();
title:
"Novita Hardini: Jangan Sampai Pariwisata Meminggirkan Warga Lokal", const req = {
category: "Technology", limit: "10",
author: "John Kontributor", page: page,
status: "Published", search: search,
date: "2024-01-15", source: "internal", // jika ingin filter image/internal
}, sort: "desc",
{ sortBy: "created_at",
title: };
"Bharatu Mardi Hadji Gugur Saat Bertugas, Diganjar Kenaikan Pangkat Luar Biasa",
category: "Partnership", const res = await getArticlePagination(req);
author: "Sarah Editor",
status: "Pending", const data = res?.data?.data || [];
date: "2024-01-14",
}, setArticles(data);
{ setTotalPage(res?.data?.meta?.totalPage || 1);
title:
"Lestari Moerdijat: Butuh Afirmasi dan Edukasi untuk Dorong Perempuan Aktif di Dunia Politik", close();
category: "Investment", }
author: "Mike Writer",
status: "Draft",
date: "2024-01-13",
},
{
title: "SEKRETARIS MAHKAMAH AGUNG LANTIK HAKIM TINGGI PENGAWAS",
category: "CSR",
author: "Jane Content",
status: "Published",
date: "2024-01-12",
},
{
title:
"Mudik Nyaman Bersama Pertamina: Layanan 24 Jam, Motoris, dan Fasilitas Lengkap",
category: "Event",
author: "John Kontributor",
status: "Rejected",
date: "2024-01-11",
},
];
const statusVariant = (status: string) => { const statusVariant = (status: string) => {
switch (status) { switch (status?.toLowerCase()) {
case "Published": case "publish":
return "bg-green-100 text-green-700"; return "bg-green-100 text-green-700";
case "Pending": case "pending":
return "bg-yellow-100 text-yellow-700"; return "bg-yellow-100 text-yellow-700";
case "Draft": case "draft":
return "bg-gray-200 text-gray-600"; return "bg-gray-200 text-gray-600";
case "Rejected": case "reject":
return "bg-red-100 text-red-600"; return "bg-red-100 text-red-600";
default: default:
return ""; return "bg-gray-200 text-gray-600";
} }
}; };
@ -106,28 +86,14 @@ export default function NewsImage() {
</Link> </Link>
</div> </div>
{/* ================= CATEGORY FILTER ================= */} {/* ================= SEARCH ================= */}
<div className="flex flex-wrap gap-3">
{categories.map((cat) => (
<Button
key={cat.name}
variant={activeCategory === cat.name ? "default" : "outline"}
className="rounded-full text-sm"
onClick={() => setActiveCategory(cat.name)}
>
{cat.name}
<span className="ml-2 text-xs opacity-70">{cat.count}</span>
</Button>
))}
</div>
{/* ================= SEARCH + FILTER ================= */}
<div className="flex gap-3"> <div className="flex gap-3">
<div className="relative flex-1"> <div className="relative flex-1">
<Search className="absolute left-3 top-3 w-4 h-4 text-slate-400" /> <Search className="absolute left-3 top-3 w-4 h-4 text-slate-400" />
<Input <Input
placeholder="Search articles by title, author, or content..." placeholder="Search articles..."
className="pl-9" className="pl-9"
onChange={(e) => setSearch(e.target.value)}
/> />
</div> </div>
@ -153,68 +119,86 @@ export default function NewsImage() {
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{articles.map((article, index) => ( {articles.length > 0 ? (
<TableRow key={index}> articles.map((article, index) => (
<TableCell className="font-medium max-w-xs"> <TableRow key={article.id}>
{article.title} <TableCell className="font-medium max-w-xs">
</TableCell> {article.title}
</TableCell>
<TableCell> <TableCell>
<Badge variant="secondary">{article.category}</Badge> <Badge variant="secondary">
</TableCell> {article?.categories
?.map((c: any) => c.title)
.join(", ")}
</Badge>
</TableCell>
<TableCell>{article.author}</TableCell> <TableCell>
{article.customCreatorName || article.createdByName}
</TableCell>
<TableCell> <TableCell>
<span <span
className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant( className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant(
article.status, article.publishStatus,
)}`} )}`}
> >
{article.status} {article.publishStatus}
</span> </span>
</TableCell> </TableCell>
<TableCell>{article.date}</TableCell> <TableCell>{formatDate(article.createdAt)}</TableCell>
<TableCell className="text-right space-x-2"> <TableCell className="text-right space-x-2">
<Button size="icon" variant="ghost"> <Button size="icon" variant="ghost">
<Eye className="w-4 h-4" /> <Eye className="w-4 h-4" />
</Button> </Button>
<Button size="icon" variant="ghost"> <Button size="icon" variant="ghost">
<Pencil className="w-4 h-4" /> <Pencil className="w-4 h-4" />
</Button> </Button>
<Button size="icon" variant="ghost"> <Button size="icon" variant="ghost">
<Trash2 className="w-4 h-4 text-red-500" /> <Trash2 className="w-4 h-4 text-red-500" />
</Button> </Button>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={6} className="text-center py-4">
No data available
</TableCell> </TableCell>
</TableRow> </TableRow>
))} )}
</TableBody> </TableBody>
</Table> </Table>
{/* ================= PAGINATION ================= */} {/* ================= PAGINATION ================= */}
<div className="flex items-center justify-between p-4 border-t text-sm text-slate-500"> <div className="flex items-center justify-between p-4 border-t text-sm text-slate-500">
<p>Showing 1 to 5 of 24 articles</p> <p>
Page {page} of {totalPage}
</p>
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" size="sm"> <Button
variant="outline"
size="sm"
disabled={page === 1}
onClick={() => setPage(page - 1)}
>
Previous Previous
</Button> </Button>
<Button size="sm" className="bg-blue-600"> <Button size="sm" className="bg-blue-600">
1 {page}
</Button> </Button>
<Button variant="outline" size="sm"> <Button
2 variant="outline"
</Button> size="sm"
disabled={page === totalPage}
<Button variant="outline" size="sm"> onClick={() => setPage(page + 1)}
3 >
</Button>
<Button variant="outline" size="sm">
Next Next
</Button> </Button>
</div> </div>

View File

@ -165,10 +165,16 @@ export function convertDateFormatNoTime(date: Date): string {
return `${year}-${month}-${day}`; return `${year}-${month}-${day}`;
} }
export function formatDate(date: Date | null) { export function formatDate(date: Date | string | null) {
if (!date) return ""; if (!date) return "";
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); const parsedDate = typeof date === "string" ? new Date(date) : date;
const day = String(date.getDate()).padStart(2, "0");
if (isNaN(parsedDate.getTime())) return "";
const year = parsedDate.getFullYear();
const month = String(parsedDate.getMonth() + 1).padStart(2, "0");
const day = String(parsedDate.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`; return `${year}-${month}-${day}`;
} }