170 lines
4.9 KiB
TypeScript
170 lines
4.9 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import type { PublicArticle } from "@/lib/articles-public";
|
|
import {
|
|
NEWS_SERVICES_TAB_ORDER,
|
|
NEWS_TAB_LABEL,
|
|
type NewsServicesTab,
|
|
} from "@/constants/news-services";
|
|
import { formatDate } from "@/utils/global";
|
|
import ArticleThumbnail from "@/components/landing-page/article-thumbnail";
|
|
|
|
const BADGE_COLORS = [
|
|
"bg-red-600",
|
|
"bg-yellow-500",
|
|
"bg-yellow-600",
|
|
"bg-[#0f3b63]",
|
|
];
|
|
|
|
function firstTag(tags: string | undefined): string {
|
|
if (!tags?.trim()) return "";
|
|
const t = tags
|
|
.split(",")
|
|
.map((s) => s.trim())
|
|
.filter(Boolean)[0];
|
|
return t ?? "";
|
|
}
|
|
|
|
function articleHref(a: PublicArticle) {
|
|
return `/news/detail/${a.id}-${a.slug}`;
|
|
}
|
|
|
|
type Props = {
|
|
id?: string;
|
|
title: string;
|
|
articlesByTab: Record<NewsServicesTab, PublicArticle[]>;
|
|
showViews?: boolean;
|
|
exploreHref?: string;
|
|
exploreLabel?: string;
|
|
};
|
|
|
|
export default function NewsServicesArticleSection({
|
|
id,
|
|
title,
|
|
articlesByTab,
|
|
showViews,
|
|
exploreHref = "#konten-terpopuler",
|
|
exploreLabel = "Lihat konten terpopuler",
|
|
}: Props) {
|
|
const defaultTab = NEWS_SERVICES_TAB_ORDER[0];
|
|
|
|
return (
|
|
<section id={id} className="py-20">
|
|
<div className="container mx-auto px-6">
|
|
<div className="mb-12 flex flex-col items-center">
|
|
<h2 className="mb-6 text-3xl font-bold">{title}</h2>
|
|
|
|
<Tabs defaultValue={defaultTab} className="w-full">
|
|
<div className="relative w-full pb-3">
|
|
<div className="flex justify-center">
|
|
<TabsList
|
|
className="gap-8 bg-transparent p-0"
|
|
variant={"line"}
|
|
>
|
|
{NEWS_SERVICES_TAB_ORDER.map((tab) => (
|
|
<TabsTrigger
|
|
key={tab}
|
|
value={tab}
|
|
className="px-0 pb-2 data-[state=active]:text-[#b07c18]"
|
|
>
|
|
{NEWS_TAB_LABEL[tab]}
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
</div>
|
|
|
|
<Link
|
|
href={exploreHref}
|
|
className="absolute top-1/2 right-0 hidden -translate-y-1/2 cursor-pointer text-sm text-muted-foreground hover:text-black md:block"
|
|
>
|
|
{exploreLabel}
|
|
</Link>
|
|
</div>
|
|
|
|
{NEWS_SERVICES_TAB_ORDER.map((tab) => (
|
|
<TabsContent key={tab} value={tab} className="mt-12">
|
|
<CardGrid
|
|
articles={articlesByTab[tab] ?? []}
|
|
showViews={showViews}
|
|
/>
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function CardGrid({
|
|
articles,
|
|
showViews,
|
|
}: {
|
|
articles: PublicArticle[];
|
|
showViews?: boolean;
|
|
}) {
|
|
if (articles.length === 0) {
|
|
return (
|
|
<p className="text-center text-muted-foreground">
|
|
Belum ada konten yang dipublikasikan untuk tab ini.
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
|
{articles.map((item) => {
|
|
const badgeClass = BADGE_COLORS[item.id % BADGE_COLORS.length];
|
|
const category =
|
|
item.categoryName?.trim() || "Berita";
|
|
const tag = firstTag(item.tags);
|
|
const dateSrc = item.publishedAt || item.createdAt;
|
|
|
|
return (
|
|
<Link
|
|
key={item.id}
|
|
href={articleHref(item)}
|
|
className="block overflow-hidden rounded-2xl bg-white shadow-sm transition-all duration-300 hover:shadow-lg"
|
|
>
|
|
<div className="relative h-[220px]">
|
|
<ArticleThumbnail
|
|
src={item.thumbnailUrl}
|
|
alt={item.title}
|
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3 p-5">
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<Badge
|
|
className={`${badgeClass} px-2 py-1 text-xs text-white`}
|
|
>
|
|
{category}
|
|
</Badge>
|
|
|
|
{tag ? (
|
|
<span className="text-xs text-muted-foreground">{tag}</span>
|
|
) : null}
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
{formatDate(dateSrc)}
|
|
{showViews && item.viewCount != null ? (
|
|
<span className="ml-2">· {item.viewCount} tayangan</span>
|
|
) : null}
|
|
</p>
|
|
|
|
<h3 className="line-clamp-3 text-sm font-semibold leading-snug transition hover:text-[#b07c18]">
|
|
{item.title}
|
|
</h3>
|
|
</div>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|