feat(frontend): refine blog and profile experience
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-09 08:53:31 +03:30
parent 8b1fc942cf
commit 8e5096d192
13 changed files with 962 additions and 898 deletions

View File

@@ -476,6 +476,33 @@ class ApiClient {
return this.request<Types.PostAssetSchema[]>(`/api/blog/admin/posts/${postId}/assets`);
}
async uploadBlogPostFeaturedImage(postId: number, file: File) {
const formData = new FormData();
formData.append('file', file);
const token = this.getStorageValue('access_token');
const response = await fetch(`${this.baseUrl}/api/blog/admin/posts/${postId}/featured-image`, {
method: 'POST',
headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: formData,
});
if (!response.ok) {
const error = (await response.json().catch(() => ({}))) as ApiErrorBody;
throw new Error(error.error || error.detail || 'Featured image upload failed');
}
return response.json() as Promise<Types.PostDetailSchema>;
}
async deleteBlogPostFeaturedImage(postId: number) {
return this.request<Types.PostDetailSchema>(`/api/blog/admin/posts/${postId}/featured-image`, {
method: 'DELETE',
});
}
async uploadBlogPostAsset(
postId: number,
file: File,

17
src/lib/blog-routes.ts Normal file
View File

@@ -0,0 +1,17 @@
const trimTrailingSlash = (value: string) => value.replace(/\/$/, "");
export function normalizeBlogSlugParam(slug: string) {
try {
return decodeURIComponent(slug);
} catch {
return slug;
}
}
export function blogPostPath(slug: string) {
return `/blog/${encodeURIComponent(normalizeBlogSlugParam(slug))}`;
}
export function blogPostUrl(baseUrl: string, slug: string) {
return `${trimTrailingSlash(baseUrl)}${blogPostPath(slug)}`;
}