fix(frontend): resolve published blog detail 404
Some checks failed
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-06-08 22:36:27 +03:30
parent 37b123838f
commit 8b1fc942cf
4 changed files with 21 additions and 13 deletions

View File

@@ -12,6 +12,8 @@ import { formatJalali } from "@/lib/utils";
type Params = Promise<{ slug: string }>; type Params = Promise<{ slug: string }>;
export const dynamic = "force-dynamic";
function cleanText(value?: string | null) { function cleanText(value?: string | null) {
if (!value) return ""; if (!value) return "";
return value.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim(); return value.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();

View File

@@ -422,7 +422,7 @@ class ApiClient {
} }
async getPost(slug: string) { async getPost(slug: string) {
return this.request<Types.PostDetailSchema>(`/api/blog/posts/${slug}`); return this.request<Types.PostDetailSchema>(`/api/blog/posts/${encodeURIComponent(slug)}`);
} }
async createPost(data: Types.PostCreateSchema) { async createPost(data: Types.PostCreateSchema) {
@@ -511,7 +511,7 @@ class ApiClient {
} }
async deletePost(slug: string) { async deletePost(slug: string) {
return this.request<Types.MessageSchema>(`/api/blog/posts/${slug}`, { return this.request<Types.MessageSchema>(`/api/blog/posts/${encodeURIComponent(slug)}`, {
method: 'DELETE', method: 'DELETE',
}); });
} }
@@ -528,11 +528,11 @@ class ApiClient {
// Comments // Comments
async getComments(slug: string) { async getComments(slug: string) {
return this.request<Types.CommentSchema[]>(`/api/blog/posts/${slug}/comments`); return this.request<Types.CommentSchema[]>(`/api/blog/posts/${encodeURIComponent(slug)}/comments`);
} }
async createComment(slug: string, data: Types.CommentCreateSchema) { async createComment(slug: string, data: Types.CommentCreateSchema) {
return this.request<Types.CommentSchema>(`/api/blog/posts/${slug}/comments`, { return this.request<Types.CommentSchema>(`/api/blog/posts/${encodeURIComponent(slug)}/comments`, {
method: 'POST', method: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
@@ -557,23 +557,23 @@ class ApiClient {
// Likes // Likes
async toggleLike(slug: string) { async toggleLike(slug: string) {
return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${slug}/like`, { return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${encodeURIComponent(slug)}/like`, {
method: 'POST', method: 'POST',
}); });
} }
async toggleSave(slug: string) { async toggleSave(slug: string) {
return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${slug}/save`, { return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${encodeURIComponent(slug)}/save`, {
method: 'POST', method: 'POST',
}); });
} }
async getBlogInteraction(slug: string) { async getBlogInteraction(slug: string) {
return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${slug}/interaction`); return this.request<Types.BlogInteractionSchema>(`/api/blog/posts/${encodeURIComponent(slug)}/interaction`);
} }
async getLikesCount(slug: string) { async getLikesCount(slug: string) {
return this.request<Types.MessageSchema>(`/api/blog/posts/${slug}/likes`); return this.request<Types.MessageSchema>(`/api/blog/posts/${encodeURIComponent(slug)}/likes`);
} }
async getMyBlogActivity() { async getMyBlogActivity() {

View File

@@ -54,11 +54,15 @@ async function requestJson<T>(
options?: { options?: {
params?: Record<string, QueryValue>; params?: Record<string, QueryValue>;
revalidate?: number; revalidate?: number;
cache?: RequestCache;
}, },
) { ) {
const response = await fetch(buildUrl(path, options?.params), { const response = await fetch(
next: { revalidate: options?.revalidate ?? DEFAULT_REVALIDATE_SECONDS }, buildUrl(path, options?.params),
}); options?.cache
? { cache: options.cache }
: { next: { revalidate: options?.revalidate ?? DEFAULT_REVALIDATE_SECONDS } },
);
if (!response.ok) { if (!response.ok) {
throw new PublicApiError(path, response.status); throw new PublicApiError(path, response.status);
@@ -80,7 +84,9 @@ export async function getPublicPosts(options?: { search?: string; limit?: number
} }
export async function getPublicPost(slug: string) { export async function getPublicPost(slug: string) {
return requestJson<Types.PostDetailSchema>(`/api/blog/posts/${encodeURIComponent(slug)}`); return requestJson<Types.PostDetailSchema>(`/api/blog/posts/${encodeURIComponent(slug)}`, {
cache: "no-store",
});
} }
export async function getPublicEvents(options?: { search?: string; limit?: number }) { export async function getPublicEvents(options?: { search?: string; limit?: number }) {

View File

@@ -80,7 +80,7 @@ export default function Blog({
) : ( ) : (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.map((post) => ( {posts.map((post) => (
<Link key={post.id} to={`/blog/${post.slug}`}> <Link key={post.id} to={`/blog/${encodeURIComponent(post.slug)}`}>
<Card className="h-full hover:shadow-lg transition-shadow"> <Card className="h-full hover:shadow-lg transition-shadow">
<CardHeader> <CardHeader>
<CardTitle className="line-clamp-2">{post.title}</CardTitle> <CardTitle className="line-clamp-2">{post.title}</CardTitle>