update form article
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
4d45e89a28
commit
cf78d137ed
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
|
@ -15,74 +15,54 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Search, Filter, Eye, Pencil, Trash2, Plus } from "lucide-react";
|
||||
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() {
|
||||
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 = [
|
||||
{ name: "All", count: 24 },
|
||||
{ name: "Technology", count: 8 },
|
||||
{ name: "Partnership", count: 5 },
|
||||
{ name: "Investment", count: 3 },
|
||||
{ name: "News", count: 4 },
|
||||
{ name: "Event", count: 2 },
|
||||
{ name: "CSR", count: 2 },
|
||||
];
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [page, search]);
|
||||
|
||||
const articles = [
|
||||
{
|
||||
title:
|
||||
"Novita Hardini: Jangan Sampai Pariwisata Meminggirkan Warga Lokal",
|
||||
category: "Technology",
|
||||
author: "John Kontributor",
|
||||
status: "Published",
|
||||
date: "2024-01-15",
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Bharatu Mardi Hadji Gugur Saat Bertugas, Diganjar Kenaikan Pangkat Luar Biasa",
|
||||
category: "Partnership",
|
||||
author: "Sarah Editor",
|
||||
status: "Pending",
|
||||
date: "2024-01-14",
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Lestari Moerdijat: Butuh Afirmasi dan Edukasi untuk Dorong Perempuan Aktif di Dunia Politik",
|
||||
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",
|
||||
},
|
||||
];
|
||||
async function fetchData() {
|
||||
loading();
|
||||
|
||||
const req = {
|
||||
limit: "10",
|
||||
page: page,
|
||||
search: search,
|
||||
source: "internal", // jika ingin filter image/internal
|
||||
sort: "desc",
|
||||
sortBy: "created_at",
|
||||
};
|
||||
|
||||
const res = await getArticlePagination(req);
|
||||
|
||||
const data = res?.data?.data || [];
|
||||
|
||||
setArticles(data);
|
||||
setTotalPage(res?.data?.meta?.totalPage || 1);
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
const statusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case "Published":
|
||||
switch (status?.toLowerCase()) {
|
||||
case "publish":
|
||||
return "bg-green-100 text-green-700";
|
||||
case "Pending":
|
||||
case "pending":
|
||||
return "bg-yellow-100 text-yellow-700";
|
||||
case "Draft":
|
||||
case "draft":
|
||||
return "bg-gray-200 text-gray-600";
|
||||
case "Rejected":
|
||||
case "reject":
|
||||
return "bg-red-100 text-red-600";
|
||||
default:
|
||||
return "";
|
||||
return "bg-gray-200 text-gray-600";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -106,28 +86,14 @@ export default function NewsImage() {
|
|||
</Link>
|
||||
</div>
|
||||
|
||||
{/* ================= CATEGORY FILTER ================= */}
|
||||
<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 ================= */}
|
||||
{/* ================= SEARCH ================= */}
|
||||
<div className="flex gap-3">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-3 w-4 h-4 text-slate-400" />
|
||||
<Input
|
||||
placeholder="Search articles by title, author, or content..."
|
||||
placeholder="Search articles..."
|
||||
className="pl-9"
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -153,68 +119,86 @@ export default function NewsImage() {
|
|||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{articles.map((article, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="font-medium max-w-xs">
|
||||
{article.title}
|
||||
</TableCell>
|
||||
{articles.length > 0 ? (
|
||||
articles.map((article, index) => (
|
||||
<TableRow key={article.id}>
|
||||
<TableCell className="font-medium max-w-xs">
|
||||
{article.title}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Badge variant="secondary">{article.category}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="secondary">
|
||||
{article?.categories
|
||||
?.map((c: any) => c.title)
|
||||
.join(", ")}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{article.author}</TableCell>
|
||||
<TableCell>
|
||||
{article.customCreatorName || article.createdByName}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<span
|
||||
className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant(
|
||||
article.status,
|
||||
)}`}
|
||||
>
|
||||
{article.status}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span
|
||||
className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant(
|
||||
article.publishStatus,
|
||||
)}`}
|
||||
>
|
||||
{article.publishStatus}
|
||||
</span>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{article.date}</TableCell>
|
||||
<TableCell>{formatDate(article.createdAt)}</TableCell>
|
||||
|
||||
<TableCell className="text-right space-x-2">
|
||||
<Button size="icon" variant="ghost">
|
||||
<Eye className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Pencil className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Trash2 className="w-4 h-4 text-red-500" />
|
||||
</Button>
|
||||
<TableCell className="text-right space-x-2">
|
||||
<Button size="icon" variant="ghost">
|
||||
<Eye className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Pencil className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Trash2 className="w-4 h-4 text-red-500" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="text-center py-4">
|
||||
No data available
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{/* ================= PAGINATION ================= */}
|
||||
<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">
|
||||
<Button variant="outline" size="sm">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={page === 1}
|
||||
onClick={() => setPage(page - 1)}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button size="sm" className="bg-blue-600">
|
||||
1
|
||||
{page}
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="sm">
|
||||
2
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="sm">
|
||||
3
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="sm">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={page === totalPage}
|
||||
onClick={() => setPage(page + 1)}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -165,10 +165,16 @@ export function convertDateFormatNoTime(date: Date): string {
|
|||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
export function formatDate(date: Date | null) {
|
||||
export function formatDate(date: Date | string | null) {
|
||||
if (!date) return "";
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
|
||||
const parsedDate = typeof date === "string" ? new Date(date) : date;
|
||||
|
||||
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}`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue