fix: fix landing page and for-you page
This commit is contained in:
parent
67fac73243
commit
1ce44acc3c
|
|
@ -6,7 +6,14 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ThumbsUp, ThumbsDown, Search, Filter, Calendar, Tag } from "lucide-react";
|
||||
import {
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
Search,
|
||||
Filter,
|
||||
Calendar,
|
||||
Tag,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { listArticles } from "@/service/landing/landing";
|
||||
|
|
@ -71,7 +78,7 @@ export default function ArticleListPage() {
|
|||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [totalData, setTotalData] = useState(0);
|
||||
|
||||
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -86,7 +93,14 @@ export default function ArticleListPage() {
|
|||
// Load articles when filters change
|
||||
useEffect(() => {
|
||||
loadArticles();
|
||||
}, [currentPage, selectedCategory, selectedType, searchTerm, startDate, endDate]);
|
||||
}, [
|
||||
currentPage,
|
||||
selectedCategory,
|
||||
selectedType,
|
||||
searchTerm,
|
||||
startDate,
|
||||
endDate,
|
||||
]);
|
||||
|
||||
// Sync bookmarks
|
||||
useEffect(() => {
|
||||
|
|
@ -107,10 +121,12 @@ export default function ArticleListPage() {
|
|||
async function loadArticles() {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const categoryId = selectedCategory === "all" ? undefined : selectedCategory;
|
||||
const typeId = selectedType === "all" ? undefined : parseInt(selectedType);
|
||||
|
||||
|
||||
const categoryId =
|
||||
selectedCategory === "all" ? undefined : selectedCategory;
|
||||
const typeId =
|
||||
selectedType === "all" ? undefined : parseInt(selectedType);
|
||||
|
||||
const response = await listArticles(
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
|
|
@ -235,11 +251,16 @@ export default function ArticleListPage() {
|
|||
|
||||
const getTypeLabel = (typeId: number) => {
|
||||
switch (typeId) {
|
||||
case 1: return "📸 Image";
|
||||
case 2: return "🎬 Video";
|
||||
case 3: return "📝 Text";
|
||||
case 4: return "🎵 Audio";
|
||||
default: return "📄 Content";
|
||||
case 1:
|
||||
return "📸 Image";
|
||||
case 2:
|
||||
return "🎬 Video";
|
||||
case 3:
|
||||
return "📝 Text";
|
||||
case 4:
|
||||
return "🎵 Audio";
|
||||
default:
|
||||
return "📄 Content";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -258,7 +279,9 @@ export default function ArticleListPage() {
|
|||
|
||||
{/* Search */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Cari Artikel</label>
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Cari Artikel
|
||||
</label>
|
||||
<form onSubmit={handleSearch} className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
|
|
@ -298,7 +321,9 @@ export default function ArticleListPage() {
|
|||
|
||||
{/* Type Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Tipe Konten</label>
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Tipe Konten
|
||||
</label>
|
||||
<select
|
||||
value={selectedType}
|
||||
onChange={(e) => {
|
||||
|
|
@ -365,11 +390,17 @@ export default function ArticleListPage() {
|
|||
{/* Main Content */}
|
||||
<div className="lg:w-3/4">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Daftar Artikel</h1>
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
Daftar Artikel
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Menampilkan {totalData} artikel
|
||||
{searchTerm && ` untuk "${searchTerm}"`}
|
||||
{selectedCategory !== "all" && ` dalam kategori "${categories.find(c => c.id === parseInt(selectedCategory))?.title || selectedCategory}"`}
|
||||
{selectedCategory !== "all" &&
|
||||
` dalam kategori "${
|
||||
categories.find((c) => c.id === parseInt(selectedCategory))
|
||||
?.title || selectedCategory
|
||||
}"`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -390,15 +421,31 @@ export default function ArticleListPage() {
|
|||
) : articles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
|
||||
{articles.map((article) => (
|
||||
<Card key={article.id} className="overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
<Card
|
||||
key={article.id}
|
||||
className="overflow-hidden hover:shadow-lg transition-shadow duration-300"
|
||||
>
|
||||
<div className="w-full h-48 relative">
|
||||
<Link href={getLink(article)}>
|
||||
<Image
|
||||
{/* <Image
|
||||
src={article.thumbnailUrl || "/placeholder.png"}
|
||||
alt={article.title || "No Title"}
|
||||
fill
|
||||
className="object-cover cursor-pointer hover:opacity-90 transition-opacity"
|
||||
/>
|
||||
/> */}
|
||||
<div className="flex items-center justify-center bg-[#bb3523] w-full h-[204px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
|
|
@ -435,10 +482,12 @@ export default function ArticleListPage() {
|
|||
className={`rounded px-4 ${
|
||||
bookmarkedIds.has(Number(article.id))
|
||||
? "bg-gray-400 cursor-not-allowed text-white"
|
||||
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||
: "bg-red-700 text-white hover:bg-red-500"
|
||||
}`}
|
||||
>
|
||||
{bookmarkedIds.has(Number(article.id)) ? "Saved" : "Save"}
|
||||
{bookmarkedIds.has(Number(article.id))
|
||||
? "Disimpan"
|
||||
: "Simpan"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -449,7 +498,9 @@ export default function ArticleListPage() {
|
|||
<Card className="p-8 text-center">
|
||||
<div className="text-gray-500">
|
||||
<p className="text-lg mb-2">Tidak ada artikel ditemukan</p>
|
||||
<p className="text-sm">Coba ubah filter atau kata kunci pencarian</p>
|
||||
<p className="text-sm">
|
||||
Coba ubah filter atau kata kunci pencarian
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
|
@ -462,14 +513,20 @@ export default function ArticleListPage() {
|
|||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
className={currentPage === 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
||||
className={
|
||||
currentPage === 1
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
const pageNumber = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i;
|
||||
const pageNumber =
|
||||
Math.max(1, Math.min(totalPages - 4, currentPage - 2)) +
|
||||
i;
|
||||
if (pageNumber > totalPages) return null;
|
||||
|
||||
|
||||
return (
|
||||
<PaginationItem key={pageNumber}>
|
||||
<PaginationLink
|
||||
|
|
@ -482,11 +539,15 @@ export default function ArticleListPage() {
|
|||
</PaginationItem>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
className={currentPage === totalPages ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
||||
className={
|
||||
currentPage === totalPages
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
|
|
|
|||
|
|
@ -432,10 +432,10 @@ export default function ArticleListPage() {
|
|||
disabled={bookmarkedIds.has(Number(article.id))}
|
||||
variant="default"
|
||||
size="sm"
|
||||
className={`rounded px-4 ${
|
||||
className={`cursor-pointer rounded px-4 ${
|
||||
bookmarkedIds.has(Number(article.id))
|
||||
? "bg-gray-400 cursor-not-allowed text-white"
|
||||
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||
: "bg-red-700 text-white hover:bg-red-500"
|
||||
}`}
|
||||
>
|
||||
{bookmarkedIds.has(Number(article.id)) ? "Saved" : "Save"}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ThumbsUp, ThumbsDown, Search, Filter, Calendar, Tag } from "lucide-react";
|
||||
import {
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
Search,
|
||||
Filter,
|
||||
Calendar,
|
||||
Tag,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { listArticles } from "@/service/landing/landing";
|
||||
|
|
@ -71,7 +78,7 @@ export default function ArticleListPage() {
|
|||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [totalData, setTotalData] = useState(0);
|
||||
|
||||
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -86,7 +93,14 @@ export default function ArticleListPage() {
|
|||
// Load articles when filters change
|
||||
useEffect(() => {
|
||||
loadArticles();
|
||||
}, [currentPage, selectedCategory, selectedType, searchTerm, startDate, endDate]);
|
||||
}, [
|
||||
currentPage,
|
||||
selectedCategory,
|
||||
selectedType,
|
||||
searchTerm,
|
||||
startDate,
|
||||
endDate,
|
||||
]);
|
||||
|
||||
// Sync bookmarks
|
||||
useEffect(() => {
|
||||
|
|
@ -107,10 +121,12 @@ export default function ArticleListPage() {
|
|||
async function loadArticles() {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const categoryId = selectedCategory === "all" ? undefined : selectedCategory;
|
||||
const typeId = selectedType === "all" ? undefined : parseInt(selectedType);
|
||||
|
||||
|
||||
const categoryId =
|
||||
selectedCategory === "all" ? undefined : selectedCategory;
|
||||
const typeId =
|
||||
selectedType === "all" ? undefined : parseInt(selectedType);
|
||||
|
||||
const response = await listArticles(
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
|
|
@ -235,11 +251,16 @@ export default function ArticleListPage() {
|
|||
|
||||
const getTypeLabel = (typeId: number) => {
|
||||
switch (typeId) {
|
||||
case 1: return "📸 Image";
|
||||
case 2: return "🎬 Video";
|
||||
case 3: return "📝 Text";
|
||||
case 4: return "🎵 Audio";
|
||||
default: return "📄 Content";
|
||||
case 1:
|
||||
return "📸 Image";
|
||||
case 2:
|
||||
return "🎬 Video";
|
||||
case 3:
|
||||
return "📝 Text";
|
||||
case 4:
|
||||
return "🎵 Audio";
|
||||
default:
|
||||
return "📄 Content";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -258,7 +279,9 @@ export default function ArticleListPage() {
|
|||
|
||||
{/* Search */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Cari Artikel</label>
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Cari Artikel
|
||||
</label>
|
||||
<form onSubmit={handleSearch} className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
|
|
@ -298,7 +321,9 @@ export default function ArticleListPage() {
|
|||
|
||||
{/* Type Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Tipe Konten</label>
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Tipe Konten
|
||||
</label>
|
||||
<select
|
||||
value={selectedType}
|
||||
onChange={(e) => {
|
||||
|
|
@ -365,11 +390,17 @@ export default function ArticleListPage() {
|
|||
{/* Main Content */}
|
||||
<div className="lg:w-3/4">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Daftar Artikel</h1>
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
Daftar Artikel
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Menampilkan {totalData} artikel
|
||||
{searchTerm && ` untuk "${searchTerm}"`}
|
||||
{selectedCategory !== "all" && ` dalam kategori "${categories.find(c => c.id === parseInt(selectedCategory))?.title || selectedCategory}"`}
|
||||
{selectedCategory !== "all" &&
|
||||
` dalam kategori "${
|
||||
categories.find((c) => c.id === parseInt(selectedCategory))
|
||||
?.title || selectedCategory
|
||||
}"`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -390,15 +421,31 @@ export default function ArticleListPage() {
|
|||
) : articles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
|
||||
{articles.map((article) => (
|
||||
<Card key={article.id} className="overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
<Card
|
||||
key={article.id}
|
||||
className="overflow-hidden hover:shadow-lg transition-shadow duration-300"
|
||||
>
|
||||
<div className="w-full h-48 relative">
|
||||
<Link href={getLink(article)}>
|
||||
<Image
|
||||
{/* <Image
|
||||
src={article.thumbnailUrl || "/placeholder.png"}
|
||||
alt={article.title || "No Title"}
|
||||
fill
|
||||
className="object-cover cursor-pointer hover:opacity-90 transition-opacity"
|
||||
/>
|
||||
/> */}
|
||||
<div className="bg-[#e0c350] flex items-center justify-center h-[204px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="90"
|
||||
height="90"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5.414a1.5 1.5 0 0 0-.44-1.06L9.647 1.439A1.5 1.5 0 0 0 8.586 1zM4 3a1 1 0 0 1 1-1h3v2.5A1.5 1.5 0 0 0 9.5 6H12v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm7.793 2H9.5a.5.5 0 0 1-.5-.5V2.207z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
|
|
@ -435,10 +482,12 @@ export default function ArticleListPage() {
|
|||
className={`rounded px-4 ${
|
||||
bookmarkedIds.has(Number(article.id))
|
||||
? "bg-gray-400 cursor-not-allowed text-white"
|
||||
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||
: "bg-red-700 text-white hover:bg-red-600"
|
||||
}`}
|
||||
>
|
||||
{bookmarkedIds.has(Number(article.id)) ? "Saved" : "Save"}
|
||||
{bookmarkedIds.has(Number(article.id))
|
||||
? "Disimpan"
|
||||
: "Simpan"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -449,7 +498,9 @@ export default function ArticleListPage() {
|
|||
<Card className="p-8 text-center">
|
||||
<div className="text-gray-500">
|
||||
<p className="text-lg mb-2">Tidak ada artikel ditemukan</p>
|
||||
<p className="text-sm">Coba ubah filter atau kata kunci pencarian</p>
|
||||
<p className="text-sm">
|
||||
Coba ubah filter atau kata kunci pencarian
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
|
@ -462,14 +513,20 @@ export default function ArticleListPage() {
|
|||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
className={currentPage === 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
||||
className={
|
||||
currentPage === 1
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
const pageNumber = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i;
|
||||
const pageNumber =
|
||||
Math.max(1, Math.min(totalPages - 4, currentPage - 2)) +
|
||||
i;
|
||||
if (pageNumber > totalPages) return null;
|
||||
|
||||
|
||||
return (
|
||||
<PaginationItem key={pageNumber}>
|
||||
<PaginationLink
|
||||
|
|
@ -482,11 +539,15 @@ export default function ArticleListPage() {
|
|||
</PaginationItem>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
className={currentPage === totalPages ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
||||
className={
|
||||
currentPage === totalPages
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ export default function ArticleListPage() {
|
|||
className={`rounded px-4 ${
|
||||
bookmarkedIds.has(Number(article.id))
|
||||
? "bg-gray-400 cursor-not-allowed text-white"
|
||||
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||
: "bg-red-700 text-white hover:bg-red-500"
|
||||
}`}
|
||||
>
|
||||
{bookmarkedIds.has(Number(article.id)) ? "Saved" : "Save"}
|
||||
|
|
|
|||
|
|
@ -14,20 +14,22 @@ import { toggleBookmark, getBookmarkSummaryForUser } from "@/service/content";
|
|||
export default function Header() {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [bookmarkedIds, setBookmarkedIds] = useState<Set<number>>(new Set());
|
||||
const [userId, setUserId] = useState<string | null>(getCookiesDecrypt("uie") || null);
|
||||
const [userId, setUserId] = useState<string | null>(
|
||||
getCookiesDecrypt("uie") || null
|
||||
);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const slug = params?.slug as string;
|
||||
|
||||
// ✅ Ambil data artikel
|
||||
// ✅ Ambil data artikel (khusus typeId = 1 -> Image)
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await listArticles(
|
||||
1,
|
||||
5,
|
||||
undefined,
|
||||
1, // ⬅️ tambahkan typeId = 1 (image only)
|
||||
undefined,
|
||||
undefined,
|
||||
"createdAt",
|
||||
|
|
@ -37,6 +39,7 @@ export default function Header() {
|
|||
let articlesData: any[] = [];
|
||||
|
||||
if (response?.error) {
|
||||
// Jika gagal, fallback tetap filter typeId = 1
|
||||
const fallback = await listData(
|
||||
"",
|
||||
"",
|
||||
|
|
@ -46,36 +49,16 @@ export default function Header() {
|
|||
"createdAt",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
"1" // ⬅️ tambahkan filter typeId = 1 di fallback juga
|
||||
);
|
||||
articlesData = fallback?.data?.data?.content || [];
|
||||
} else {
|
||||
articlesData = response?.data?.data || [];
|
||||
}
|
||||
|
||||
const transformed = articlesData.map((article: any) => ({
|
||||
id: article.id,
|
||||
title: article.title,
|
||||
categoryName:
|
||||
article.categoryName ||
|
||||
(article.categories && article.categories[0]?.title) ||
|
||||
"",
|
||||
createdAt: article.createdAt,
|
||||
smallThumbnailLink: article.thumbnailUrl,
|
||||
fileTypeId: article.typeId,
|
||||
clientName: article.clientName,
|
||||
categories: article.categories,
|
||||
label:
|
||||
article.typeId === 1
|
||||
? "Image"
|
||||
: article.typeId === 2
|
||||
? "Video"
|
||||
: article.typeId === 3
|
||||
? "Text"
|
||||
: article.typeId === 4
|
||||
? "Audio"
|
||||
: "",
|
||||
}));
|
||||
const transformed = articlesData.map((article: any) =>
|
||||
itemTransform(article)
|
||||
);
|
||||
|
||||
setData(transformed);
|
||||
} catch (error) {
|
||||
|
|
@ -92,7 +75,7 @@ export default function Header() {
|
|||
setUserId(currentUserId);
|
||||
|
||||
if (!currentUserId) {
|
||||
setBookmarkedIds(new Set());
|
||||
setBookmarkedIds(new Set());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +101,7 @@ export default function Header() {
|
|||
};
|
||||
|
||||
fetchBookmarks();
|
||||
}, [userId]);
|
||||
}, [userId]);
|
||||
|
||||
return (
|
||||
<section className="max-w-[1350px] mx-auto px-4">
|
||||
|
|
@ -175,6 +158,33 @@ export default function Header() {
|
|||
);
|
||||
}
|
||||
|
||||
// 🔹 Helper function untuk transform data
|
||||
function itemTransform(article: any) {
|
||||
return {
|
||||
id: article.id,
|
||||
title: article.title,
|
||||
categoryName:
|
||||
article.categoryName ||
|
||||
(article.categories && article.categories[0]?.title) ||
|
||||
"",
|
||||
createdAt: article.createdAt,
|
||||
smallThumbnailLink: article.thumbnailUrl,
|
||||
fileTypeId: article.typeId,
|
||||
clientName: article.clientName,
|
||||
categories: article.categories,
|
||||
label:
|
||||
article.typeId === 1
|
||||
? "Image"
|
||||
: article.typeId === 2
|
||||
? "Video"
|
||||
: article.typeId === 3
|
||||
? "Text"
|
||||
: article.typeId === 4
|
||||
? "Audio"
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
function Card({
|
||||
item,
|
||||
isBig = false,
|
||||
|
|
@ -197,20 +207,7 @@ function Card({
|
|||
setIsBookmarked(isInitiallyBookmarked);
|
||||
}, [isInitiallyBookmarked]);
|
||||
|
||||
const getLink = () => {
|
||||
switch (item?.fileTypeId) {
|
||||
case 1:
|
||||
return `/content/image/detail/${item?.id}`;
|
||||
case 2:
|
||||
return `/content/video/detail/${item?.id}`;
|
||||
case 3:
|
||||
return `/content/text/detail/${item?.id}`;
|
||||
case 4:
|
||||
return `/content/audio/detail/${item?.id}`;
|
||||
default:
|
||||
return "#";
|
||||
}
|
||||
};
|
||||
const getLink = () => `/content/image/detail/${item?.id}`;
|
||||
|
||||
const handleToggleBookmark = async () => {
|
||||
const roleId = Number(getCookiesDecrypt("urie"));
|
||||
|
|
@ -237,10 +234,8 @@ function Card({
|
|||
confirmButtonColor: "#d33",
|
||||
});
|
||||
} else {
|
||||
// ✅ Toggle berhasil di server
|
||||
const nowBookmarked = !isBookmarked;
|
||||
setIsBookmarked(nowBookmarked);
|
||||
|
||||
if (nowBookmarked) onSaved?.(item.id);
|
||||
else onRemoved?.(item.id);
|
||||
|
||||
|
|
@ -292,7 +287,7 @@ function Card({
|
|||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-2">
|
||||
<div className="p-6 space-y-2">
|
||||
<div className="flex items-center gap-2 text-xs font-semibold flex-wrap">
|
||||
<span className="bg-emerald-600 text-white px-2 py-0.5 rounded">
|
||||
{item.clientName}
|
||||
|
|
@ -323,10 +318,16 @@ function Card({
|
|||
</h3>
|
||||
</Link>
|
||||
|
||||
<div className="flex justify-between items-center pt-2">
|
||||
<div className="flex justify-between items-center pt-4">
|
||||
<div className="flex gap-2 text-gray-500">
|
||||
<ThumbsUp size={18} className="cursor-pointer hover:text-[#F60100]" />
|
||||
<ThumbsDown size={18} className="cursor-pointer hover:text-red-600" />
|
||||
<ThumbsUp
|
||||
size={18}
|
||||
className="cursor-pointer hover:text-[#F60100]"
|
||||
/>
|
||||
<ThumbsDown
|
||||
size={18}
|
||||
className="cursor-pointer hover:text-red-600"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -338,11 +339,7 @@ function Card({
|
|||
: "bg-[#F60100] text-white hover:bg-[#c90000]"
|
||||
}`}
|
||||
>
|
||||
{isSaving
|
||||
? "Saving..."
|
||||
: isBookmarked
|
||||
? "Saved"
|
||||
: "Save"}
|
||||
{isSaving ? "Menyimpan" : isBookmarked ? "Tersimpan" : "Simpan"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -481,10 +481,15 @@ export default function MediaUpdate() {
|
|||
|
||||
{/* Caption / info */}
|
||||
<div className="p-3">
|
||||
<div className="flex gap-2 mb-1">
|
||||
<span className="text-xs text-white px-2 py-0.5 rounded bg-blue-600">
|
||||
<div className="flex items-center gap-2 text-xs font-semibold flex-wrap mb-2">
|
||||
<span className="text-xs text-white px-2 py-0.5 rounded bg-emerald-600">
|
||||
{item.clientName || "Tanpa Kategori"}
|
||||
</span>
|
||||
<span className="text-orange-600">
|
||||
{item.categories
|
||||
?.map((cat: any) => cat.title)
|
||||
.join(", ")}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mb-1">
|
||||
{formatTanggal(item.createdAt)}
|
||||
|
|
@ -508,7 +513,7 @@ export default function MediaUpdate() {
|
|||
className={`rounded px-4 ${
|
||||
bookmarkedIds.has(Number(item.id))
|
||||
? "bg-gray-400 cursor-not-allowed text-white"
|
||||
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||
: "bg-red-700 text-white hover:bg-red-500"
|
||||
}`}
|
||||
>
|
||||
{bookmarkedIds.has(Number(item.id))
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import { DynamicLogoTenant } from "./dynamic-logo-tenant";
|
|||
|
||||
const NAV_ITEMS = [
|
||||
{ label: "Beranda", href: "/" },
|
||||
{ label: "Untuk Anda", href: "/public/for-you" },
|
||||
{ label: "Untuk Anda", href: "/for-you" },
|
||||
{ label: "Mengikuti", href: "/auth" },
|
||||
{ label: "Publikasi", href: "/publikasi" },
|
||||
{ label: "Jadwal", href: "/public/schedule" },
|
||||
{ label: "Jadwal", href: "/schedule" },
|
||||
];
|
||||
|
||||
const PUBLIKASI_SUBMENU = [
|
||||
{ label: "K/L", href: "/public/publication/kl" },
|
||||
{ label: "BUMN", href: "/in/public/publication/bumn" },
|
||||
{ label: "Pemerintah Daerah", href: "/public/publication/pemerintah-daerah" },
|
||||
{ label: "K/L", href: "/in/publication/kl" },
|
||||
{ label: "BUMN", href: "/in/publication/bumn" },
|
||||
{ label: "Pemerintah Daerah", href: "/in/publication/pemerintah-daerah" },
|
||||
];
|
||||
|
||||
export default function Navbar() {
|
||||
|
|
@ -100,7 +100,7 @@ export default function Navbar() {
|
|||
if (!checkLoginStatus()) {
|
||||
router.push("/auth");
|
||||
} else {
|
||||
router.push("/in/public/for-you");
|
||||
router.push("/in/for-you");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -267,7 +267,7 @@ export default function Navbar() {
|
|||
if (!checkLoginStatus()) {
|
||||
router.push("/auth");
|
||||
} else {
|
||||
router.push("/public/for-you");
|
||||
router.push("/for-you");
|
||||
}
|
||||
} else {
|
||||
router.push(item.href);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default function PublicationKlLayout() {
|
|||
<div className="border rounded p-4 bg-white">
|
||||
<SidebarFilterForYou
|
||||
activeTab={activeTab}
|
||||
onTabChange={(tab) =>
|
||||
onTabChange={(tab: any) =>
|
||||
setActiveTab(tab as "My Collections" | "Archives" | "Favorites")
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useState, useEffect } from "react";
|
|||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Archive, Star } from "lucide-react";
|
||||
import { Archive, Star, Trash2, Box, Undo2, HeartOff } from "lucide-react";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { getBookmarks } from "@/service/content";
|
||||
|
|
@ -14,7 +14,7 @@ import "swiper/css";
|
|||
import "swiper/css/navigation";
|
||||
import { Navigation } from "swiper/modules";
|
||||
|
||||
// Format tanggal
|
||||
// 🔹 Format tanggal WIB
|
||||
function formatTanggal(dateString: string) {
|
||||
if (!dateString) return "";
|
||||
return (
|
||||
|
|
@ -32,6 +32,7 @@ function formatTanggal(dateString: string) {
|
|||
);
|
||||
}
|
||||
|
||||
// 🔹 Link detail konten
|
||||
function getLink(item: BookmarkItem) {
|
||||
switch (item.article?.typeId) {
|
||||
case 1:
|
||||
|
|
@ -55,6 +56,9 @@ type ForYouCardGridProps = {
|
|||
export default function ForYouCardGrid({ filterType }: ForYouCardGridProps) {
|
||||
const [bookmarks, setBookmarks] = useState<BookmarkItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [contentType, setContentType] = useState<
|
||||
"image" | "video" | "text" | "audio"
|
||||
>("image");
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -78,188 +82,182 @@ export default function ForYouCardGrid({ filterType }: ForYouCardGridProps) {
|
|||
}
|
||||
};
|
||||
|
||||
const grouped = {
|
||||
image: bookmarks.filter((b) => b.article?.typeId === 1).slice(0, ),
|
||||
video: bookmarks.filter((b) => b.article?.typeId === 2).slice(0, 5),
|
||||
text: bookmarks.filter((b) => b.article?.typeId === 3).slice(0, 5),
|
||||
audio: bookmarks.filter((b) => b.article?.typeId === 4).slice(0, 5),
|
||||
// 🔹 Filter konten berdasarkan tab aktif
|
||||
const filtered = bookmarks.filter((b) => {
|
||||
const t = b.article?.typeId;
|
||||
switch (contentType) {
|
||||
case "image":
|
||||
return t === 1;
|
||||
case "video":
|
||||
return t === 2;
|
||||
case "text":
|
||||
return t === 3;
|
||||
case "audio":
|
||||
return t === 4;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 🔹 Aksi tombol (dummy sementara)
|
||||
const handleAction = (action: string, title: string) => {
|
||||
MySwal.fire({
|
||||
icon: "info",
|
||||
title: action,
|
||||
text: `${title} berhasil di${action.toLowerCase()}.`,
|
||||
timer: 1200,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
};
|
||||
|
||||
const renderImageVideoCard = (bookmark: BookmarkItem) => (
|
||||
<div className="rounded-xl shadow-md bg-white hover:shadow-lg transition-shadow overflow-hidden">
|
||||
<div className="relative w-full h-[200px]">
|
||||
<Link href={getLink(bookmark)}>
|
||||
<Image
|
||||
src={bookmark.article?.thumbnailUrl || "/placeholder.png"}
|
||||
alt={bookmark.article?.title || "No Title"}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</Link>
|
||||
const renderButtons = (bookmark: BookmarkItem) => {
|
||||
const title = bookmark.article?.title || "Konten";
|
||||
|
||||
if (filterType === "Archives") {
|
||||
// 🗃️ Unarchive + Delete
|
||||
return (
|
||||
<div className="flex gap-2 mt-3 justify-center">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Unarchive", title)}
|
||||
className="flex items-center text-xs gap-1 text-blue-600 border-blue-200 hover:bg-blue-50 cursor-pointer"
|
||||
>
|
||||
<Undo2 className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Unarchive</p>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Delete", title)}
|
||||
className="flex items-center text-xs gap-1 text-red-600 border-red-200 hover:bg-red-50 cursor-pointer"
|
||||
>
|
||||
<Trash2 className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Delete</p>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (filterType === "Favorites") {
|
||||
// 📦 Archive + 💔 Unfavorite
|
||||
return (
|
||||
<div className="flex gap-2 mt-3 justify-center">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Archive", title)}
|
||||
className="flex items-center text-xs gap-1 text-gray-700 border-gray-300 hover:bg-gray-100 cursor-pointer"
|
||||
>
|
||||
<Archive className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Archive</p>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Unfavorite", title)}
|
||||
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50 cursor-pointer"
|
||||
>
|
||||
<HeartOff className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Unfavorite</p>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Default: My Collections → Archive + Favorite
|
||||
return (
|
||||
<div className="flex gap-2 mt-3 justify-center">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Archive", title)}
|
||||
className="flex items-center text-xs gap-1 text-gray-700 border-gray-300 hover:bg-gray-100 cursor-pointer"
|
||||
>
|
||||
<Archive className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Archive</p>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleAction("Favorite", title)}
|
||||
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50 cursor-pointer"
|
||||
>
|
||||
<Star className="w-[10px] h-[10px]" />
|
||||
<p className="text-[10px]">Favorite</p>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderCard = (bookmark: BookmarkItem) => (
|
||||
<div className="rounded-xl shadow-md bg-white hover:shadow-lg transition-shadow overflow-hidden">
|
||||
{/* Gambar / ikon */}
|
||||
{bookmark.article?.typeId === 3 ? (
|
||||
// 📝 TEXT
|
||||
<div className="bg-[#e0c350] flex items-center justify-center h-[200px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5.414a1.5 1.5 0 0 0-.44-1.06L9.647 1.439A1.5 1.5 0 0 0 8.586 1zM4 3a1 1 0 0 1 1-1h3v2.5A1.5 1.5 0 0 0 9.5 6H12v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm7.793 2H9.5a.5.5 0 0 1-.5-.5V2.207z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
) : bookmark.article?.typeId === 4 ? (
|
||||
// 🎵 AUDIO
|
||||
<div className="flex items-center justify-center bg-[#bb3523] w-full h-[200px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
) : (
|
||||
// 📸 / 🎬
|
||||
<div className="relative w-full h-[200px]">
|
||||
<Link href={getLink(bookmark)}>
|
||||
<Image
|
||||
src={bookmark.article?.thumbnailUrl || "/placeholder.png"}
|
||||
alt={bookmark.article?.title || "No Title"}
|
||||
fill
|
||||
className="object-cover cursor-pointer hover:opacity-90 transition"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Caption */}
|
||||
<div className="p-3">
|
||||
<p className="text-xs text-gray-500 mb-1">
|
||||
Disimpan: {formatTanggal(bookmark.createdAt)}
|
||||
</p>
|
||||
<Link href={getLink(bookmark)}>
|
||||
<p className="text-sm font-semibold line-clamp-2 cursor-pointer hover:text-blue-600 transition-colors">
|
||||
<p className="text-sm font-semibold line-clamp-2 hover:text-blue-600 transition-colors">
|
||||
{bookmark.article?.title}
|
||||
</p>
|
||||
</Link>
|
||||
<div className="flex gap-2 mt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-gray-700 border-gray-300 hover:bg-gray-100"
|
||||
>
|
||||
<Archive className="w-4 h-4" /> Archive
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50"
|
||||
>
|
||||
<Star className="w-4 h-4" /> Favorite
|
||||
</Button>
|
||||
</div>
|
||||
{renderButtons(bookmark)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderTextCard = (bookmark: BookmarkItem) => (
|
||||
<div className="cursor-pointer rounded-lg shadow-md overflow-hidden bg-white dark:bg-black dark:border dark:border-gray-500 hover:shadow-lg transition-shadow">
|
||||
{/* Background kuning & ikon dokumen */}
|
||||
<div className="bg-[#e0c350] flex items-center justify-center h-[170px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5.414a1.5 1.5 0 0 0-.44-1.06L9.647 1.439A1.5 1.5 0 0 0 8.586 1zM4 3a1 1 0 0 1 1-1h3v2.5A1.5 1.5 0 0 0 9.5 6H12v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm7.793 2H9.5a.5.5 0 0 1-.5-.5V2.207z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Konten bawah */}
|
||||
<div className="p-4 flex flex-col gap-2">
|
||||
<p className="text-xs text-gray-500 mb-1">
|
||||
Disimpan: {formatTanggal(bookmark.createdAt)}
|
||||
</p>
|
||||
<Link
|
||||
href={getLink(bookmark)}
|
||||
className="font-semibold text-gray-900 dark:text-white text-base leading-snug line-clamp-3"
|
||||
>
|
||||
{bookmark.article?.title}
|
||||
</Link>
|
||||
<div className="flex gap-2 mt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-gray-700 border-gray-300 hover:bg-gray-100"
|
||||
>
|
||||
<Archive className="w-4 h-4" /> Archive
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50"
|
||||
>
|
||||
<Star className="w-4 h-4" /> Favorite
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderAudioCard = (bookmark: BookmarkItem) => (
|
||||
<div className="cursor-pointer bg-white dark:bg-black dark:border dark:border-gray-500 rounded-xl shadow-md hover:shadow-lg transition-shadow overflow-hidden">
|
||||
{/* Background merah & ikon audio */}
|
||||
<div className="flex items-center justify-center bg-[#bb3523] w-full h-[170px] text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="120"
|
||||
height="120"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Caption */}
|
||||
<Link href={getLink(bookmark)} className="p-4">
|
||||
<p className="text-xs text-gray-500 mb-1">
|
||||
Disimpan: {formatTanggal(bookmark.createdAt)}
|
||||
</p>
|
||||
<p className="text-base font-semibold text-black dark:text-white line-clamp-3">
|
||||
{bookmark.article?.title}
|
||||
</p>
|
||||
</Link>
|
||||
<div className="flex gap-2 mx-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-gray-700 border-gray-300 hover:bg-gray-100"
|
||||
>
|
||||
<Archive className="w-4 h-4" /> Archive
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50"
|
||||
>
|
||||
<Star className="w-4 h-4" /> Favorite
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderSection = (
|
||||
title: string,
|
||||
items: BookmarkItem[],
|
||||
type: "imagevideo" | "text" | "audio"
|
||||
) => {
|
||||
if (!items.length) return null;
|
||||
|
||||
return (
|
||||
<div className="mb-10">
|
||||
<h2 className="text-xl font-bold text-gray-800 mb-4">{title}</h2>
|
||||
<Swiper
|
||||
spaceBetween={20}
|
||||
slidesPerView={1.2}
|
||||
breakpoints={{
|
||||
640: { slidesPerView: 2.2 },
|
||||
1024: { slidesPerView: 3.2 },
|
||||
1280: { slidesPerView: 4.2 },
|
||||
}}
|
||||
navigation
|
||||
modules={[Navigation]}
|
||||
>
|
||||
{items.map((bookmark) => (
|
||||
<SwiperSlide key={bookmark.id}>
|
||||
{type === "imagevideo"
|
||||
? renderImageVideoCard(bookmark)
|
||||
: type === "text"
|
||||
? renderTextCard(bookmark)
|
||||
: renderAudioCard(bookmark)}
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
<div className="text-center py-12 text-gray-500">Memuat konten...</div>
|
||||
);
|
||||
|
||||
if (bookmarks.length === 0)
|
||||
if (filtered.length === 0)
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-gray-400 text-6xl mb-4">📂</div>
|
||||
|
|
@ -270,12 +268,51 @@ export default function ForYouCardGrid({ filterType }: ForYouCardGridProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-10">
|
||||
{renderSection("📸 Image", grouped.image, "imagevideo")}
|
||||
{renderSection("🎬 Video", grouped.video, "imagevideo")}
|
||||
{renderSection("📝 Text", grouped.text, "text")}
|
||||
{renderSection("🎵 Audio", grouped.audio, "audio")}
|
||||
</div>
|
||||
<section className="bg-white px-4 py-6 border rounded-md border-[#CDD5DF]">
|
||||
{/* Tab Filter Konten */}
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-2xl p-4 border-2 border-blue-100 shadow-lg">
|
||||
<div className="flex flex-wrap justify-center gap-2">
|
||||
{["image", "video", "audio", "text"].map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => setContentType(type as any)}
|
||||
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
||||
contentType === type
|
||||
? "bg-gradient-to-r from-orange-500 to-red-600 text-white shadow-lg ring-2 ring-orange-300"
|
||||
: "bg-white text-orange-600 border-2 border-orange-200 hover:border-orange-400 hover:shadow-md"
|
||||
}`}
|
||||
>
|
||||
{type === "image"
|
||||
? "📸 Foto"
|
||||
: type === "video"
|
||||
? "🎬 Audio Visual"
|
||||
: type === "audio"
|
||||
? "🎵 Audio"
|
||||
: "📝 Text"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Slider Konten */}
|
||||
<Swiper
|
||||
modules={[Navigation]}
|
||||
navigation
|
||||
spaceBetween={20}
|
||||
slidesPerView={1.2}
|
||||
breakpoints={{
|
||||
640: { slidesPerView: 2.2 },
|
||||
1024: { slidesPerView: 3.2 },
|
||||
1280: { slidesPerView: 4.2 },
|
||||
}}
|
||||
>
|
||||
{filtered.map((bookmark) => (
|
||||
<SwiperSlide key={bookmark.id}>{renderCard(bookmark)}</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ export default function SidebarFilterForYou({
|
|||
return (
|
||||
<div>
|
||||
<div className="flex gap-2 items-center border-b-2 mb-3">
|
||||
<h2 className="font-semibold text-lg">Create New Collection </h2>
|
||||
<Plus size={20} />
|
||||
<h2 className="font-semibold text-lg">Collection </h2>
|
||||
{/* <Plus size={20} /> */}
|
||||
</div>
|
||||
<ul className="space-y-6 text-[16px] text-[#757575]">
|
||||
{filters.map((item, idx) => (
|
||||
|
|
|
|||
Loading…
Reference in New Issue