fix: fixed category filtering problem
This commit is contained in:
+7
-3
@@ -2,14 +2,18 @@ import { BlogContent } from "@/components/blog-content";
|
|||||||
import { getBlogPageData } from "@/data/blog";
|
import { getBlogPageData } from "@/data/blog";
|
||||||
|
|
||||||
type BlogPageProps = {
|
type BlogPageProps = {
|
||||||
searchParams?: Promise<{ page?: string }>;
|
searchParams?: Promise<{ page?: string | string[]; category?: string | string[] }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function BlogPage({ searchParams }: BlogPageProps) {
|
export default async function BlogPage({ searchParams }: BlogPageProps) {
|
||||||
const resolved = searchParams ? await searchParams : undefined;
|
const resolved = searchParams ? await searchParams : undefined;
|
||||||
const page = Number(resolved?.page ?? "1");
|
const pageParam = Array.isArray(resolved?.page) ? resolved?.page[0] : resolved?.page;
|
||||||
|
const categoryParam = Array.isArray(resolved?.category)
|
||||||
|
? resolved?.category[0]
|
||||||
|
: resolved?.category;
|
||||||
|
const page = Number(pageParam ?? "1");
|
||||||
const currentPage = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1;
|
const currentPage = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1;
|
||||||
const data = await getBlogPageData(currentPage, 12);
|
const data = await getBlogPageData(currentPage, 12, categoryParam);
|
||||||
|
|
||||||
return <BlogContent data={data} />;
|
return <BlogContent data={data} />;
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-10
@@ -16,8 +16,30 @@ type BlogContentProps = {
|
|||||||
data: BlogPageData;
|
data: BlogPageData;
|
||||||
};
|
};
|
||||||
|
|
||||||
function pageHref(page: number) {
|
function pageHref(page: number, category: string) {
|
||||||
return page <= 1 ? "/blog" : `/blog?page=${page}`;
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
params.set("page", String(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category !== "All") {
|
||||||
|
params.set("category", category);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = params.toString();
|
||||||
|
return query ? `/blog?${query}` : "/blog";
|
||||||
|
}
|
||||||
|
|
||||||
|
function categoryHref(category: string) {
|
||||||
|
if (category === "All") {
|
||||||
|
return "/blog";
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("category", category);
|
||||||
|
|
||||||
|
return `/blog?${params.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BlogContent({ data }: BlogContentProps) {
|
export function BlogContent({ data }: BlogContentProps) {
|
||||||
@@ -45,14 +67,15 @@ export function BlogContent({ data }: BlogContentProps) {
|
|||||||
<Card className="rounded-sm border-border p-4">
|
<Card className="rounded-sm border-border p-4">
|
||||||
<Typography variant="large">Kategoriler</Typography>
|
<Typography variant="large">Kategoriler</Typography>
|
||||||
<div className="mt-3 flex flex-wrap gap-2">
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
{data.categories.map((category, index) => (
|
{data.categories.map((category) => (
|
||||||
|
<Link key={category} href={categoryHref(category)}>
|
||||||
<Badge
|
<Badge
|
||||||
key={category}
|
variant={category === data.selectedCategory ? "default" : "outline"}
|
||||||
variant={index === 0 ? "default" : "outline"}
|
className="cursor-pointer rounded-sm"
|
||||||
className="rounded-sm"
|
|
||||||
>
|
>
|
||||||
{category}
|
{category}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -114,7 +137,9 @@ export function BlogContent({ data }: BlogContentProps) {
|
|||||||
) : (
|
) : (
|
||||||
<Card className="rounded-sm border-border p-5">
|
<Card className="rounded-sm border-border p-5">
|
||||||
<Typography variant="p" className="text-muted-foreground">
|
<Typography variant="p" className="text-muted-foreground">
|
||||||
Henüz blog yazısı bulunmuyor.
|
{data.selectedCategory === "All"
|
||||||
|
? "Henüz blog yazısı bulunmuyor."
|
||||||
|
: `"${data.selectedCategory}" kategorisinde henüz blog yazısı bulunmuyor.`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
@@ -123,14 +148,17 @@ export function BlogContent({ data }: BlogContentProps) {
|
|||||||
<PaginationContent className="justify-start">
|
<PaginationContent className="justify-start">
|
||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationPrevious
|
<PaginationPrevious
|
||||||
href={pageHref(Math.max(1, data.currentPage - 1))}
|
href={pageHref(Math.max(1, data.currentPage - 1), data.selectedCategory)}
|
||||||
aria-disabled={data.currentPage <= 1}
|
aria-disabled={data.currentPage <= 1}
|
||||||
/>
|
/>
|
||||||
</PaginationItem>
|
</PaginationItem>
|
||||||
|
|
||||||
{pageNumbers.map((page) => (
|
{pageNumbers.map((page) => (
|
||||||
<PaginationItem key={page}>
|
<PaginationItem key={page}>
|
||||||
<PaginationLink href={pageHref(page)} isActive={page === data.currentPage}>
|
<PaginationLink
|
||||||
|
href={pageHref(page, data.selectedCategory)}
|
||||||
|
isActive={page === data.currentPage}
|
||||||
|
>
|
||||||
{page}
|
{page}
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
</PaginationItem>
|
</PaginationItem>
|
||||||
@@ -138,7 +166,7 @@ export function BlogContent({ data }: BlogContentProps) {
|
|||||||
|
|
||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationNext
|
<PaginationNext
|
||||||
href={pageHref(Math.min(data.totalPages, data.currentPage + 1))}
|
href={pageHref(Math.min(data.totalPages, data.currentPage + 1), data.selectedCategory)}
|
||||||
aria-disabled={data.currentPage >= data.totalPages}
|
aria-disabled={data.currentPage >= data.totalPages}
|
||||||
/>
|
/>
|
||||||
</PaginationItem>
|
</PaginationItem>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "Yazilim"
|
|||||||
date: "2026-02-01"
|
date: "2026-02-01"
|
||||||
readTime: "8 min read"
|
readTime: "8 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "Yazılım geliştirme sürecinde yalnızca kod yazmıyoruz; aynı zamanda takım iletişimini ve proje geçmişini...."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/1*WQOgajPE2m4aYuVC3SZoFQ.jpeg"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/1*WQOgajPE2m4aYuVC3SZoFQ.jpeg"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "Testing"
|
|||||||
date: "2026-02-22"
|
date: "2026-02-22"
|
||||||
readTime: "9 min read"
|
readTime: "9 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "Frontend tarafında kaliteyi korumanın en sağlam yolu testtir. Bu yazıda, farklı amaçlara hizmet eden..."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*uNpkjRqVlaB2WbDl.png"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*uNpkjRqVlaB2WbDl.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "TypeScript"
|
|||||||
date: "2026-02-18"
|
date: "2026-02-18"
|
||||||
readTime: "8 min read"
|
readTime: "8 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "JavaScript'ten TypeScript'e geçiş, özellikle React projelerinde kod kalitesini gözle görülür şekilde artırıyor..."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*yRK5eL_pqabAhrn7"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*yRK5eL_pqabAhrn7"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "UX"
|
|||||||
date: "2026-02-05"
|
date: "2026-02-05"
|
||||||
readTime: "6 min read"
|
readTime: "6 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "Tasarım dünyasında, ürünlerimizin kullanıcılarımızın ihtiyaçlarını en iyi şekilde karşılamasını istemek..."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*BEVhh2Et54wytkaA.jpg"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*BEVhh2Et54wytkaA.jpg"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "Frontend"
|
|||||||
date: "2026-02-14"
|
date: "2026-02-14"
|
||||||
readTime: "11 min read"
|
readTime: "11 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "Frontend dünyasında React, Vue ve Angular gibi güçlü seçenekler var. Son yıllarda bu listeye güçlü bir alternatif..."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*Dx04pr9plejiCcWI.png"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*Dx04pr9plejiCcWI.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "Frontend"
|
|||||||
date: "2026-02-10"
|
date: "2026-02-10"
|
||||||
readTime: "10 min read"
|
readTime: "10 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "Tailwind CSS, son dönemde front-end geliştiriciler arasında çok popüler hale geldi. Klasik CSS..."
|
||||||
coverImage: "/news/performance.svg"
|
coverImage: "/news/performance.svg"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ category: "UX"
|
|||||||
date: "2026-02-26"
|
date: "2026-02-26"
|
||||||
readTime: "12 min read"
|
readTime: "12 min read"
|
||||||
author: "Poyraz Avsever"
|
author: "Poyraz Avsever"
|
||||||
excerpt: "Bu yazida temel kavramlari, pratik ornekleri ve uygulayabileceginiz ipuclarini sade bir dille bulabilirsiniz."
|
excerpt: "UX (Kullanıcı Deneyimi) tasarımı, teknolojiyle etkileşime geçen herkesin deneyimini iyileştirmeye..."
|
||||||
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*5OUFz00g3w5ee2lV.jpg"
|
coverImage: "https://miro.medium.com/v2/resize:fit:1400/format:webp/0*5OUFz00g3w5ee2lV.jpg"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+26
-7
@@ -44,6 +44,7 @@ export type BlogPageData = {
|
|||||||
news: BlogNewsItem[];
|
news: BlogNewsItem[];
|
||||||
articles: BlogArticleItem[];
|
articles: BlogArticleItem[];
|
||||||
categories: string[];
|
categories: string[];
|
||||||
|
selectedCategory: string;
|
||||||
podcastGroups: BlogPodcastGroup[];
|
podcastGroups: BlogPodcastGroup[];
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@@ -74,6 +75,10 @@ function sortByDateDesc<T extends { date: string; id: string }>(items: T[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeCategory(value: string) {
|
||||||
|
return value.trim().toLocaleLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function mapEpisodeToPodcastItem(episode: PodcastEpisode): BlogPodcastItem {
|
function mapEpisodeToPodcastItem(episode: PodcastEpisode): BlogPodcastItem {
|
||||||
return {
|
return {
|
||||||
id: `${episode.podcast}-${episode.slug}`,
|
id: `${episode.podcast}-${episode.slug}`,
|
||||||
@@ -115,13 +120,12 @@ export async function getHomeBlogNews(limit = 3): Promise<BlogNewsItem[]> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBlogPageData(page = 1, pageSize = 12): Promise<BlogPageData> {
|
export async function getBlogPageData(
|
||||||
|
page = 1,
|
||||||
|
pageSize = 12,
|
||||||
|
selectedCategoryParam?: string,
|
||||||
|
): Promise<BlogPageData> {
|
||||||
const articles = await getAllBlogArticles();
|
const articles = await getAllBlogArticles();
|
||||||
const totalPages = Math.max(1, Math.ceil(Math.max(articles.length, 1) / pageSize));
|
|
||||||
const currentPage = Math.min(Math.max(1, page), totalPages);
|
|
||||||
const start = (currentPage - 1) * pageSize;
|
|
||||||
const paginated = articles.slice(start, start + pageSize);
|
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
"All",
|
"All",
|
||||||
...new Set(
|
...new Set(
|
||||||
@@ -130,11 +134,25 @@ export async function getBlogPageData(page = 1, pageSize = 12): Promise<BlogPage
|
|||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
const categoryByNormalized = new Map(
|
||||||
|
categories.map((category) => [normalizeCategory(category), category]),
|
||||||
|
);
|
||||||
|
const requestedCategory = selectedCategoryParam?.trim();
|
||||||
|
const selectedCategory =
|
||||||
|
(requestedCategory && categoryByNormalized.get(normalizeCategory(requestedCategory))) || "All";
|
||||||
|
const filteredArticles =
|
||||||
|
selectedCategory === "All"
|
||||||
|
? articles
|
||||||
|
: articles.filter((item) => normalizeCategory(item.category) === normalizeCategory(selectedCategory));
|
||||||
|
const totalPages = Math.max(1, Math.ceil(Math.max(filteredArticles.length, 1) / pageSize));
|
||||||
|
const currentPage = Math.min(Math.max(1, page), totalPages);
|
||||||
|
const start = (currentPage - 1) * pageSize;
|
||||||
|
const paginated = filteredArticles.slice(start, start + pageSize);
|
||||||
|
|
||||||
const podcastCollections = await getPodcastCollections();
|
const podcastCollections = await getPodcastCollections();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
news: articles.slice(0, 4).map((item) => ({
|
news: filteredArticles.slice(0, 4).map((item) => ({
|
||||||
id: `blog-news-${item.slug}`,
|
id: `blog-news-${item.slug}`,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
category: item.category,
|
category: item.category,
|
||||||
@@ -144,6 +162,7 @@ export async function getBlogPageData(page = 1, pageSize = 12): Promise<BlogPage
|
|||||||
})),
|
})),
|
||||||
articles: paginated,
|
articles: paginated,
|
||||||
categories,
|
categories,
|
||||||
|
selectedCategory,
|
||||||
totalPages,
|
totalPages,
|
||||||
currentPage,
|
currentPage,
|
||||||
podcastGroups: [
|
podcastGroups: [
|
||||||
|
|||||||
Reference in New Issue
Block a user