update filter table, ui dashboard
continuous-integration/drone/push Build encountered an error
Details
continuous-integration/drone/push Build encountered an error
Details
This commit is contained in:
parent
a698dfa52a
commit
165ccdb718
|
|
@ -63,8 +63,8 @@ export default function ImageFilterPage() {
|
||||||
<FloatingMenuNews />
|
<FloatingMenuNews />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div className="">
|
||||||
{Array.from({ length: 6 }).map((_, i) => (
|
{Array.from({ length: 1 }).map((_, i) => (
|
||||||
<ImageCard key={i} />
|
<ImageCard key={i} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,50 +2,97 @@
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getListArticle } from "@/service/article";
|
||||||
|
|
||||||
|
type Article = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
categoryName: string;
|
||||||
|
createdAt: string;
|
||||||
|
slug: string;
|
||||||
|
thumbnailUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default function ImageCard() {
|
export default function ImageCard() {
|
||||||
const slug = "bharatu-mardi-hadji-gugur-saat-bertugas";
|
const [articles, setArticles] = useState<Article[]>([]);
|
||||||
|
const [page] = useState(1);
|
||||||
|
const [showData] = useState("6");
|
||||||
|
const [search] = useState("");
|
||||||
|
|
||||||
|
async function initState() {
|
||||||
|
const req = {
|
||||||
|
limit: showData,
|
||||||
|
page,
|
||||||
|
search,
|
||||||
|
categorySlug: "",
|
||||||
|
sort: "desc",
|
||||||
|
isPublish: true,
|
||||||
|
sortBy: "created_at",
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getListArticle(req);
|
||||||
|
setArticles(res?.data?.data || []);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error get article:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/details/${slug}?type=image`}>
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-md transition duration-300 overflow-hidden">
|
{articles.map((item) => (
|
||||||
{/* IMAGE */}
|
<Link key={item.id} href={`/details/${item.slug}?type=image`}>
|
||||||
<div className="relative h-[200px] w-full">
|
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-md transition duration-300 overflow-hidden">
|
||||||
<Image
|
{/* IMAGE */}
|
||||||
src="/image/novita2.png"
|
<div className="relative h-[200px] w-full">
|
||||||
alt="news"
|
<Image
|
||||||
fill
|
src={item.thumbnailUrl || "/image/novita2.png"}
|
||||||
className="object-cover"
|
alt={item.title}
|
||||||
/>
|
fill
|
||||||
</div>
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* CONTENT */}
|
{/* CONTENT */}
|
||||||
<div className="p-5 space-y-3">
|
<div className="p-5 space-y-3">
|
||||||
{/* BADGE + TAG */}
|
{/* BADGE + TAG */}
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="bg-red-600 text-white px-2 py-[3px] rounded-md font-medium">
|
<span className="bg-red-600 text-white px-2 py-[3px] rounded-md font-medium">
|
||||||
POLRI
|
POLRI
|
||||||
</span>
|
</span>
|
||||||
<span className="text-gray-500 uppercase tracking-wide">
|
<span className="text-gray-500 uppercase tracking-wide">
|
||||||
SEPUTAR PRESTASI
|
{item.categoryName}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATE */}
|
||||||
|
<p className="text-xs text-gray-400">
|
||||||
|
{new Date(item.createdAt).toLocaleDateString("id-ID", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* TITLE */}
|
||||||
|
<h3 className="font-semibold text-[15px] leading-snug line-clamp-2 hover:text-[#966314] transition">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* EXCERPT */}
|
||||||
|
<p className="text-sm text-gray-500 line-clamp-2 leading-relaxed">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
{/* DATE */}
|
))}
|
||||||
<p className="text-xs text-gray-400">02 Februari 2024</p>
|
</div>
|
||||||
|
|
||||||
{/* TITLE */}
|
|
||||||
<h3 className="font-semibold text-[15px] leading-snug line-clamp-2 hover:text-[#966314] transition">
|
|
||||||
Bharatu Mardi Hadji Gugur Saat Bertugas, Diganjar Kenaikan Pangkat
|
|
||||||
Luar Biasa
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{/* EXCERPT */}
|
|
||||||
<p className="text-sm text-gray-500 line-clamp-2 leading-relaxed">
|
|
||||||
Jakarta - Kapolri Jenderal Polisi Drs. Listyo Sigit Prabowo
|
|
||||||
memberikan kenaikan pangkat luar biasa anumerta kepada...
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const Option = ({
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
className={`relative flex h-12 w-full px-3 items-center rounded-xl transition-all duration-200 cursor-pointer group ${
|
className={`relative flex h-12 w-full px-3 items-center rounded-xl transition-all duration-200 cursor-pointer group ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-gradient-to-r from-[#966314] to-[#966314] text-white shadow-lg shadow-emerald-500/25"
|
? "bg-gradient-to-r from-[#966314] to-[#966314] text-white shadow-xl shadow-[#9663144D]/25"
|
||||||
: "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800"
|
: "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800"
|
||||||
}`}
|
}`}
|
||||||
whileHover={{ scale: 1.02 }}
|
whileHover={{ scale: 1.02 }}
|
||||||
|
|
|
||||||
|
|
@ -344,24 +344,88 @@ export default function DashboardContainer() {
|
||||||
value: 24,
|
value: 24,
|
||||||
growth: "+12%",
|
growth: "+12%",
|
||||||
iconBg: "bg-blue-600",
|
iconBg: "bg-blue-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
>
|
||||||
|
<path d="M15 2.5V4c0 1.414 0 2.121.44 2.56C15.878 7 16.585 7 18 7h1.5"></path>
|
||||||
|
<path d="M4 16V8c0-2.828 0-4.243.879-5.121C5.757 2 7.172 2 10 2h4.172c.408 0 .613 0 .797.076c.183.076.328.22.617.51l3.828 3.828c.29.29.434.434.51.618c.076.183.076.388.076.796V16c0 2.828 0 4.243-.879 5.121C18.243 22 16.828 22 14 22h-4c-2.828 0-4.243 0-5.121-.879C4 20.243 4 18.828 4 16m4-5h8m-8 3h8m-8 3h4.17"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Pending Approval",
|
title: "Pending Approval",
|
||||||
value: 8,
|
value: 8,
|
||||||
growth: "+3",
|
growth: "+3",
|
||||||
iconBg: "bg-yellow-500",
|
iconBg: "bg-yellow-500",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g fill="none">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Published",
|
title: "Published",
|
||||||
value: 16,
|
value: 16,
|
||||||
growth: "+5",
|
growth: "+5",
|
||||||
iconBg: "bg-green-600",
|
iconBg: "bg-green-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10.5 15.25A.74.74 0 0 1 10 15l-3-3a.75.75 0 0 1 1-1l2.47 2.47L19 5a.75.75 0 0 1 1 1l-9 9a.74.74 0 0 1-.5.25"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 21a9 9 0 0 1-7.87-4.66a8.7 8.7 0 0 1-1.07-3.41a9 9 0 0 1 4.6-8.81a8.7 8.7 0 0 1 3.41-1.07a8.9 8.9 0 0 1 3.55.34a.75.75 0 1 1-.43 1.43a7.6 7.6 0 0 0-3-.28a7.4 7.4 0 0 0-2.84.89a7.5 7.5 0 0 0-2.2 1.84a7.42 7.42 0 0 0-1.64 5.51a7.4 7.4 0 0 0 .89 2.84a7.5 7.5 0 0 0 1.84 2.2a7.42 7.42 0 0 0 5.51 1.64a7.4 7.4 0 0 0 2.84-.89a7.5 7.5 0 0 0 2.2-1.84a7.42 7.42 0 0 0 1.64-5.51a.75.75 0 1 1 1.57-.15a9 9 0 0 1-4.61 8.81A8.7 8.7 0 0 1 12.93 21z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Rejected",
|
title: "Rejected",
|
||||||
value: 2,
|
value: 2,
|
||||||
growth: "-1",
|
growth: "-1",
|
||||||
iconBg: "bg-red-600",
|
iconBg: "bg-red-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m12 13.4l2.9 2.9q.275.275.7.275t.7-.275t.275-.7t-.275-.7L13.4 12l2.9-2.9q.275-.275.275-.7t-.275-.7t-.7-.275t-.7.275L12 10.6L9.1 7.7q-.275-.275-.7-.275t-.7.275t-.275.7t.275.7l2.9 2.9l-2.9 2.9q-.275.275-.275.7t.275.7t.7.275t.7-.275zm0 8.6q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -406,20 +470,25 @@ export default function DashboardContainer() {
|
||||||
{stats.map((card, i) => (
|
{stats.map((card, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="bg-white rounded-2xl shadow border p-6 flex justify-between items-start"
|
className="bg-white rounded-2xl shadow border p-6 flex-row items-start"
|
||||||
>
|
>
|
||||||
<div>
|
<div className="flex flex-row items-center justify-between">
|
||||||
<p className="text-sm text-slate-500">{card.title}</p>
|
<div
|
||||||
<h2 className="text-3xl font-bold text-slate-800 mt-2">
|
className={`w-10 h-10 rounded-xl flex items-center justify-center text-white ${card.iconBg}`}
|
||||||
{card.value}
|
>
|
||||||
</h2>
|
{card.icon}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right">
|
|
||||||
<p className="text-sm text-green-600 font-medium">
|
<p className="text-sm text-green-600 font-medium">
|
||||||
{card.growth}
|
{card.growth}
|
||||||
</p>
|
</p>
|
||||||
<div className={`w-10 h-10 rounded-xl mt-3 ${card.iconBg}`} />
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 className="text-3xl font-bold text-slate-800 mt-2">
|
||||||
|
{card.value}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-slate-500">{card.title}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -473,19 +542,19 @@ export default function DashboardContainer() {
|
||||||
<div className="bg-[#966314] rounded-2xl shadow p-6 text-white space-y-4">
|
<div className="bg-[#966314] rounded-2xl shadow p-6 text-white space-y-4">
|
||||||
<h2 className="text-lg font-semibold">Quick Actions</h2>
|
<h2 className="text-lg font-semibold">Quick Actions</h2>
|
||||||
|
|
||||||
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-xl text-sm font-medium">
|
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-md text-sm font-medium text-start pl-3">
|
||||||
+ Create New Article
|
+ Create New Article
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-xl text-sm font-medium">
|
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-md text-sm font-medium text-start pl-3">
|
||||||
+ Update Product
|
+ Update Product
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-xl text-sm font-medium">
|
<button className="w-full border border-white bg-[#966314] hover:bg-[#966314] transition py-3 rounded-md text-sm font-medium text-start pl-3">
|
||||||
+ Upload Media
|
+ Upload Media
|
||||||
</button>
|
</button>
|
||||||
|
<p className="border-b-2" />
|
||||||
<button className="w-full bg-white text-amber-800 py-3 rounded-xl text-sm font-semibold">
|
<button className="w-full bg-[#EBE2D2] text-amber-800 py-3 rounded-md text-sm font-semibold ">
|
||||||
View All Actions
|
View All Actions
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -500,25 +569,89 @@ export default function DashboardContainer() {
|
||||||
title: "Pending Review",
|
title: "Pending Review",
|
||||||
value: 12,
|
value: 12,
|
||||||
growth: "+3",
|
growth: "+3",
|
||||||
color: "bg-yellow-500",
|
iconBg: "bg-yellow-500",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g fill="none">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Approved Today",
|
title: "Approved Today",
|
||||||
value: 8,
|
value: 8,
|
||||||
growth: "+5",
|
growth: "+5",
|
||||||
color: "bg-green-600",
|
iconBg: "bg-green-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10.5 15.25A.74.74 0 0 1 10 15l-3-3a.75.75 0 0 1 1-1l2.47 2.47L19 5a.75.75 0 0 1 1 1l-9 9a.74.74 0 0 1-.5.25"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 21a9 9 0 0 1-7.87-4.66a8.7 8.7 0 0 1-1.07-3.41a9 9 0 0 1 4.6-8.81a8.7 8.7 0 0 1 3.41-1.07a8.9 8.9 0 0 1 3.55.34a.75.75 0 1 1-.43 1.43a7.6 7.6 0 0 0-3-.28a7.4 7.4 0 0 0-2.84.89a7.5 7.5 0 0 0-2.2 1.84a7.42 7.42 0 0 0-1.64 5.51a7.4 7.4 0 0 0 .89 2.84a7.5 7.5 0 0 0 1.84 2.2a7.42 7.42 0 0 0 5.51 1.64a7.4 7.4 0 0 0 2.84-.89a7.5 7.5 0 0 0 2.2-1.84a7.42 7.42 0 0 0 1.64-5.51a.75.75 0 1 1 1.57-.15a9 9 0 0 1-4.61 8.81A8.7 8.7 0 0 1 12.93 21z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Total Published",
|
title: "Total Published",
|
||||||
value: 156,
|
value: 156,
|
||||||
growth: "+12%",
|
growth: "+12%",
|
||||||
color: "bg-blue-600",
|
iconBg: "bg-blue-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
>
|
||||||
|
<path d="M15 2.5V4c0 1.414 0 2.121.44 2.56C15.878 7 16.585 7 18 7h1.5"></path>
|
||||||
|
<path d="M4 16V8c0-2.828 0-4.243.879-5.121C5.757 2 7.172 2 10 2h4.172c.408 0 .613 0 .797.076c.183.076.328.22.617.51l3.828 3.828c.29.29.434.434.51.618c.076.183.076.388.076.796V16c0 2.828 0 4.243-.879 5.121C18.243 22 16.828 22 14 22h-4c-2.828 0-4.243 0-5.121-.879C4 20.243 4 18.828 4 16m4-5h8m-8 3h8m-8 3h4.17"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Rejected",
|
title: "Rejected",
|
||||||
value: 5,
|
value: 5,
|
||||||
growth: "-1",
|
growth: "-1",
|
||||||
color: "bg-red-600",
|
iconBg: "bg-red-600",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m12 13.4l2.9 2.9q.275.275.7.275t.7-.275t.275-.7t-.275-.7L13.4 12l2.9-2.9q.275-.275.275-.7t-.275-.7t-.7-.275t-.7.275L12 10.6L9.1 7.7q-.275-.275-.7-.275t-.7.275t-.275.7t.275.7l2.9 2.9l-2.9 2.9q-.275.275-.275.7t.275.7t.7.275t.7-.275zm0 8.6q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -544,16 +677,66 @@ export default function DashboardContainer() {
|
||||||
status: "Approved",
|
status: "Approved",
|
||||||
title: "Technology Summit Event",
|
title: "Technology Summit Event",
|
||||||
time: "10 mins ago",
|
time: "10 mins ago",
|
||||||
|
iconBg: "bg-[#DCFCE7]",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g fill="currentColor">
|
||||||
|
<path d="M10.243 16.314L6 12.07l1.414-1.414l2.829 2.828l5.656-5.657l1.415 1.415z"></path>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M1 12C1 5.925 5.925 1 12 1s11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12m11 9a9 9 0 1 1 0-18a9 9 0 0 1 0 18"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: "Rejected",
|
status: "Rejected",
|
||||||
title: "Product Update Draft",
|
title: "Product Update Draft",
|
||||||
time: "25 mins ago",
|
time: "25 mins ago",
|
||||||
|
iconBg: "bg-[#FFE2E2]",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m12 13.4l2.9 2.9q.275.275.7.275t.7-.275t.275-.7t-.275-.7L13.4 12l2.9-2.9q.275-.275.275-.7t-.275-.7t-.7-.275t-.7.275L12 10.6L9.1 7.7q-.275-.275-.7-.275t-.7.275t-.275.7t.275.7l2.9 2.9l-2.9 2.9q-.275.275-.275.7t.275.7t.7.275t.7-.275zm0 8.6q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: "Approved",
|
status: "Approved",
|
||||||
title: "Partner Logo Update",
|
title: "Partner Logo Update",
|
||||||
time: "1 hour ago",
|
time: "1 hour ago",
|
||||||
|
iconBg: "bg-[#DCFCE7]",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g fill="currentColor">
|
||||||
|
<path d="M10.243 16.314L6 12.07l1.414-1.414l2.829 2.828l5.656-5.657l1.415 1.415z"></path>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M1 12C1 5.925 5.925 1 12 1s11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12m11 9a9 9 0 1 1 0-18a9 9 0 0 1 0 18"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -574,20 +757,25 @@ export default function DashboardContainer() {
|
||||||
{stats.map((card, i) => (
|
{stats.map((card, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="bg-white rounded-2xl shadow border p-6 flex justify-between items-start"
|
className="bg-white rounded-2xl shadow border p-6 flex flex-col"
|
||||||
>
|
>
|
||||||
<div>
|
<div className="flex flex-row items-center justify-between">
|
||||||
<p className="text-sm text-slate-500">{card.title}</p>
|
<div
|
||||||
<h2 className="text-3xl font-bold text-slate-800 mt-2">
|
className={`w-10 h-10 rounded-md flex items-center justify-center text-white ${card.iconBg}`}
|
||||||
{card.value}
|
>
|
||||||
</h2>
|
{card.icon}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right">
|
|
||||||
<p className="text-sm text-green-600 font-medium">
|
<p className="text-sm text-green-600 font-medium">
|
||||||
{card.growth}
|
{card.growth}
|
||||||
</p>
|
</p>
|
||||||
<div className={`w-10 h-10 rounded-xl mt-3 ${card.color}`} />
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 className="text-3xl font-bold text-slate-800 mt-2">
|
||||||
|
{card.value}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-slate-500">{card.title}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -656,18 +844,27 @@ export default function DashboardContainer() {
|
||||||
key={i}
|
key={i}
|
||||||
className="border rounded-xl p-4 flex justify-between items-center"
|
className="border rounded-xl p-4 flex justify-between items-center"
|
||||||
>
|
>
|
||||||
<div>
|
<div className="flex flex-row items-center gap-3">
|
||||||
<p
|
<div
|
||||||
className={`text-sm font-medium ${
|
className={`w-10 h-10 rounded-md flex items-center justify-center
|
||||||
item.status === "Approved"
|
${item.status === "Approved" ? "text-green-600" : "text-red-600"}
|
||||||
? "text-green-600"
|
${item.iconBg}`}
|
||||||
: "text-red-600"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{item.status}
|
{item.icon}
|
||||||
</p>
|
</div>
|
||||||
<p className="text-sm text-slate-700">{item.title}</p>
|
<div>
|
||||||
<p className="text-xs text-slate-500">{item.time}</p>
|
<p
|
||||||
|
className={`text-sm font-medium ${
|
||||||
|
item.status === "Approved"
|
||||||
|
? "text-green-600"
|
||||||
|
: "text-red-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.status}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-slate-700">{item.title}</p>
|
||||||
|
<p className="text-xs text-slate-500">{item.time}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ 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 { getArticleByCategory, getArticlePagination } from "@/service/article";
|
||||||
import { formatDate } from "@/utils/global";
|
import { formatDate } from "@/utils/global";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
|
@ -26,15 +26,28 @@ export default function NewsImage() {
|
||||||
const [totalPage, setTotalPage] = useState(1);
|
const [totalPage, setTotalPage] = useState(1);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [levelId, setLevelId] = useState<string | undefined>();
|
const [levelId, setLevelId] = useState<string | undefined>();
|
||||||
|
const [categories, setupCategory] = useState<any[]>([]);
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState<number | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ulne = Cookies.get("ulne");
|
const ulne = Cookies.get("ulne");
|
||||||
setLevelId(ulne);
|
setLevelId(ulne);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchCategory();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchCategory = async () => {
|
||||||
|
const res = await getArticleByCategory();
|
||||||
|
if (res?.data?.data) {
|
||||||
|
setupCategory(res.data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, search]);
|
}, [page, search, selectedCategory]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -42,12 +55,13 @@ export default function NewsImage() {
|
||||||
const req = {
|
const req = {
|
||||||
limit: "10",
|
limit: "10",
|
||||||
page: page,
|
page: page,
|
||||||
search: search,
|
title: search,
|
||||||
source: "internal", // jika ingin filter image/internal
|
source: "internal",
|
||||||
|
categoryId: selectedCategory,
|
||||||
|
search: "",
|
||||||
sort: "desc",
|
sort: "desc",
|
||||||
sortBy: "created_at",
|
sortBy: "created_at",
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await getArticlePagination(req);
|
const res = await getArticlePagination(req);
|
||||||
|
|
||||||
const data = res?.data?.data || [];
|
const data = res?.data?.data || [];
|
||||||
|
|
@ -58,8 +72,20 @@ export default function NewsImage() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStatus = (article: any) => {
|
||||||
|
if (article.isDraft) return "Draft";
|
||||||
|
|
||||||
|
if (article.publishStatus?.toLowerCase() === "cancel") {
|
||||||
|
return "Pending";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.isPublish) return "Published";
|
||||||
|
|
||||||
|
return "Pending";
|
||||||
|
};
|
||||||
|
|
||||||
const statusVariant = (status: string) => {
|
const statusVariant = (status: string) => {
|
||||||
const value = status?.toLowerCase();
|
const value = status.toLowerCase();
|
||||||
|
|
||||||
if (value === "published") {
|
if (value === "published") {
|
||||||
return "bg-green-100 text-green-700";
|
return "bg-green-100 text-green-700";
|
||||||
|
|
@ -69,13 +95,26 @@ export default function NewsImage() {
|
||||||
return "bg-yellow-100 text-yellow-700";
|
return "bg-yellow-100 text-yellow-700";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === "cancel") {
|
if (value === "draft") {
|
||||||
return "bg-red-100 text-red-700";
|
return "bg-gray-200 text-gray-600";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "bg-gray-200 text-gray-600";
|
return "bg-gray-200 text-gray-600";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCategoryCount = (title: string) => {
|
||||||
|
return articles.filter((article) =>
|
||||||
|
article.categories?.some((c: any) => c.title === title),
|
||||||
|
).length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredArticles =
|
||||||
|
selectedCategory === null
|
||||||
|
? articles
|
||||||
|
: articles.filter((article) =>
|
||||||
|
article.categories?.some((c: any) => c.id === selectedCategory),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* ================= HEADER ================= */}
|
{/* ================= HEADER ================= */}
|
||||||
|
|
@ -97,7 +136,42 @@ export default function NewsImage() {
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant={selectedCategory === null ? "default" : "outline"}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedCategory(null);
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
All
|
||||||
|
<span className="text-xs bg-slate-200 px-2 py-0.5 rounded-full">
|
||||||
|
{articles.length}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{categories.map((cat: any) => (
|
||||||
|
<Button
|
||||||
|
key={cat.id}
|
||||||
|
size="sm"
|
||||||
|
variant={selectedCategory === cat.id ? "default" : "outline"}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedCategory(cat.id);
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{cat.title}
|
||||||
|
<span className="text-xs bg-white text-black px-2 py-0.5 rounded-full ">
|
||||||
|
{getCategoryCount(cat.title)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
{/* ================= SEARCH ================= */}
|
{/* ================= SEARCH ================= */}
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
|
|
@ -121,7 +195,7 @@ export default function NewsImage() {
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Article</TableHead>
|
<TableHead className="pl-3">Article</TableHead>
|
||||||
<TableHead>Category</TableHead>
|
<TableHead>Category</TableHead>
|
||||||
<TableHead>Author</TableHead>
|
<TableHead>Author</TableHead>
|
||||||
<TableHead>Status</TableHead>
|
<TableHead>Status</TableHead>
|
||||||
|
|
@ -131,10 +205,10 @@ export default function NewsImage() {
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{articles.length > 0 ? (
|
{filteredArticles.length > 0 ? (
|
||||||
articles.map((article, index) => (
|
filteredArticles.map((article, index) => (
|
||||||
<TableRow key={article.id}>
|
<TableRow key={article.id}>
|
||||||
<TableCell className="font-medium max-w-xs">
|
<TableCell className="font-medium max-w-xs pl-3">
|
||||||
{article.title}
|
{article.title}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
|
@ -151,13 +225,19 @@ export default function NewsImage() {
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<span
|
{(() => {
|
||||||
className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant(
|
const status = getStatus(article);
|
||||||
article.publishStatus,
|
|
||||||
)}`}
|
return (
|
||||||
>
|
<span
|
||||||
{article.publishStatus}
|
className={`px-3 py-1 text-xs rounded-full font-medium ${statusVariant(
|
||||||
</span>
|
status,
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
{status}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>{formatDate(article.createdAt)}</TableCell>
|
<TableCell>{formatDate(article.createdAt)}</TableCell>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export async function getListArticle(props: PaginationRequest) {
|
||||||
isBanner,
|
isBanner,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return await httpGet(
|
return await httpGetInterceptor(
|
||||||
`/articles?limit=${limit}&page=${page}&isPublish=${
|
`/articles?limit=${limit}&page=${page}&isPublish=${
|
||||||
isPublish === undefined ? "" : isPublish
|
isPublish === undefined ? "" : isPublish
|
||||||
}&title=${search}&startDate=${startDate || ""}&endDate=${
|
}&title=${search}&startDate=${startDate || ""}&endDate=${
|
||||||
|
|
@ -30,7 +30,6 @@ export async function getListArticle(props: PaginationRequest) {
|
||||||
}&categoryId=${category || ""}&sortBy=${sortBy || "created_at"}&sort=${
|
}&categoryId=${category || ""}&sortBy=${sortBy || "created_at"}&sort=${
|
||||||
sort || "desc"
|
sort || "desc"
|
||||||
}&category=${categorySlug || ""}&isBanner=${isBanner || ""}`,
|
}&category=${categorySlug || ""}&isBanner=${isBanner || ""}`,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,10 +37,10 @@ export async function getArticlePagination(props: PaginationRequest) {
|
||||||
const {
|
const {
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
search,
|
title,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
category,
|
categoryId,
|
||||||
sortBy,
|
sortBy,
|
||||||
sort,
|
sort,
|
||||||
categorySlug,
|
categorySlug,
|
||||||
|
|
@ -51,9 +50,9 @@ export async function getArticlePagination(props: PaginationRequest) {
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return await httpGet(
|
return await httpGet(
|
||||||
`/articles?limit=${limit}&page=${page}&title=${search}&startDate=${
|
`/articles?limit=${limit}&page=${page}&title=${title || ""}&startDate=${
|
||||||
startDate || ""
|
startDate || ""
|
||||||
}&endDate=${endDate || ""}&categoryId=${category || ""}&source=${
|
}&endDate=${endDate || ""}&categoryId=${categoryId || ""}&source=${
|
||||||
source || ""
|
source || ""
|
||||||
}&isPublish=${isPublish !== undefined ? isPublish : ""}&sortBy=${
|
}&isPublish=${isPublish !== undefined ? isPublish : ""}&sortBy=${
|
||||||
sortBy || "created_at"
|
sortBy || "created_at"
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,8 @@ export type PaginationRequest = {
|
||||||
search: string;
|
search: string;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
endDate?: string;
|
endDate?: string;
|
||||||
|
categoryId: any;
|
||||||
|
title: string;
|
||||||
isPublish?: boolean;
|
isPublish?: boolean;
|
||||||
category?: string;
|
category?: string;
|
||||||
sortBy?: string;
|
sortBy?: string;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue