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";
|
"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>
|
||||||
|
|
|
||||||
|
|
@ -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}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue