update
This commit is contained in:
437
gnx-react/lib/api/blogService.ts
Normal file
437
gnx-react/lib/api/blogService.ts
Normal file
@@ -0,0 +1,437 @@
|
||||
import { API_CONFIG } from '../config/api';
|
||||
|
||||
// Types for Blog API
|
||||
export interface BlogAuthor {
|
||||
id: number;
|
||||
name: string;
|
||||
email?: string;
|
||||
bio?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
export interface BlogCategory {
|
||||
id: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
display_order: number;
|
||||
posts_count?: number;
|
||||
}
|
||||
|
||||
export interface BlogTag {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface BlogPost {
|
||||
id: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
content?: string;
|
||||
excerpt: string;
|
||||
thumbnail?: string;
|
||||
featured_image?: string;
|
||||
author?: BlogAuthor;
|
||||
author_name?: string;
|
||||
category?: BlogCategory;
|
||||
category_title?: string;
|
||||
category_slug?: string;
|
||||
tags?: BlogTag[];
|
||||
meta_description?: string;
|
||||
meta_keywords?: string;
|
||||
published: boolean;
|
||||
featured: boolean;
|
||||
views_count: number;
|
||||
reading_time: number;
|
||||
published_at: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
related_posts?: BlogPost[];
|
||||
}
|
||||
|
||||
export interface BlogPostListResponse {
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
results: BlogPost[];
|
||||
}
|
||||
|
||||
export interface BlogComment {
|
||||
id: number;
|
||||
post: number;
|
||||
name: string;
|
||||
email: string;
|
||||
content: string;
|
||||
parent?: number;
|
||||
is_approved: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
replies?: BlogComment[];
|
||||
}
|
||||
|
||||
export interface BlogCommentCreateData {
|
||||
post: number;
|
||||
name: string;
|
||||
email: string;
|
||||
content: string;
|
||||
parent?: number;
|
||||
}
|
||||
|
||||
// Helper function to build query string
|
||||
const buildQueryString = (params: Record<string, any>): string => {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
searchParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
return searchParams.toString();
|
||||
};
|
||||
|
||||
// Blog API functions
|
||||
export const blogService = {
|
||||
// Get all blog posts with optional filtering
|
||||
getPosts: async (params?: {
|
||||
category?: string;
|
||||
tag?: string;
|
||||
author?: number;
|
||||
search?: string;
|
||||
featured?: boolean;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}): Promise<BlogPostListResponse> => {
|
||||
try {
|
||||
const queryString = params ? buildQueryString(params) : '';
|
||||
const url = `${API_CONFIG.BASE_URL}/api/blog/posts/${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get a single blog post by slug
|
||||
getPostBySlug: async (slug: string): Promise<BlogPost> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/${slug}/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog post:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get featured blog posts
|
||||
getFeaturedPosts: async (): Promise<BlogPost[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/featured/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching featured posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get latest blog posts
|
||||
getLatestPosts: async (limit: number = 5): Promise<BlogPost[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/latest/?limit=${limit}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching latest posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get popular blog posts
|
||||
getPopularPosts: async (limit: number = 5): Promise<BlogPost[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/popular/?limit=${limit}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching popular posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get related posts for a specific post
|
||||
getRelatedPosts: async (postSlug: string): Promise<BlogPost[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/${postSlug}/related/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching related posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get all blog categories
|
||||
getCategories: async (): Promise<BlogCategory[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return Array.isArray(data) ? data : data.results || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog categories:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get categories with posts
|
||||
getCategoriesWithPosts: async (): Promise<BlogCategory[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/with_posts/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching categories with posts:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get a single category by slug
|
||||
getCategoryBySlug: async (slug: string): Promise<BlogCategory> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/${slug}/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog category:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get all blog tags
|
||||
getTags: async (): Promise<BlogTag[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/tags/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return Array.isArray(data) ? data : data.results || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog tags:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get posts by tag
|
||||
getPostsByTag: async (tagSlug: string): Promise<BlogPostListResponse> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/tags/${tagSlug}/posts/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching posts by tag:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get all blog authors
|
||||
getAuthors: async (): Promise<BlogAuthor[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/authors/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return Array.isArray(data) ? data : data.results || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching blog authors:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get posts by author
|
||||
getPostsByAuthor: async (authorId: number): Promise<BlogPostListResponse> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/authors/${authorId}/posts/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching posts by author:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get comments for a post
|
||||
getComments: async (postId: number): Promise<BlogComment[]> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/comments/?post=${postId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return Array.isArray(data) ? data : data.results || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching comments:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Create a new comment
|
||||
createComment: async (commentData: BlogCommentCreateData): Promise<{ message: string; data: BlogComment }> => {
|
||||
try {
|
||||
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/comments/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(commentData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error creating comment:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
375
gnx-react/lib/api/caseStudyService.ts
Normal file
375
gnx-react/lib/api/caseStudyService.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
import { API_CONFIG } from '../config/api';
|
||||
|
||||
// Types for Case Study API
|
||||
export interface CaseStudyCategory {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
display_order: number;
|
||||
case_studies_count?: number;
|
||||
}
|
||||
|
||||
export interface Client {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
logo?: string;
|
||||
description?: string;
|
||||
website?: string;
|
||||
}
|
||||
|
||||
export interface CaseStudyImage {
|
||||
id: number;
|
||||
image: string;
|
||||
caption?: string;
|
||||
display_order: number;
|
||||
}
|
||||
|
||||
export interface CaseStudyProcess {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
step_number: number;
|
||||
}
|
||||
|
||||
export interface CaseStudy {
|
||||
id: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
subtitle?: string;
|
||||
description?: string;
|
||||
excerpt: string;
|
||||
thumbnail?: string;
|
||||
featured_image?: string;
|
||||
poster_image?: string;
|
||||
project_image?: string;
|
||||
project_overview?: string;
|
||||
site_map_content?: string;
|
||||
category?: CaseStudyCategory;
|
||||
category_name?: string;
|
||||
category_slug?: string;
|
||||
client?: Client;
|
||||
client_name?: string;
|
||||
gallery_images?: CaseStudyImage[];
|
||||
process_steps?: CaseStudyProcess[];
|
||||
meta_description?: string;
|
||||
meta_keywords?: string;
|
||||
published: boolean;
|
||||
featured: boolean;
|
||||
views_count: number;
|
||||
display_order: number;
|
||||
published_at: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
related_case_studies?: CaseStudy[];
|
||||
}
|
||||
|
||||
export interface CaseStudyListResponse {
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
results: CaseStudy[];
|
||||
}
|
||||
|
||||
// Helper function to build query string
|
||||
const buildQueryString = (params: Record<string, any>): string => {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
searchParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
return searchParams.toString();
|
||||
};
|
||||
|
||||
// Case Study API functions
|
||||
export const caseStudyService = {
|
||||
// Get all case studies with optional filtering
|
||||
getCaseStudies: async (params?: {
|
||||
category?: string;
|
||||
client?: string;
|
||||
search?: string;
|
||||
featured?: boolean;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}): Promise<CaseStudyListResponse> => {
|
||||
try {
|
||||
const queryString = params ? buildQueryString(params) : '';
|
||||
const url = queryString
|
||||
? `${API_CONFIG.BASE_URL}/api/case-studies/case-studies/?${queryString}`
|
||||
: `${API_CONFIG.BASE_URL}/api/case-studies/case-studies/`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching case studies:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get a single case study by slug
|
||||
getCaseStudyBySlug: async (slug: string): Promise<CaseStudy> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/${slug}/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching case study ${slug}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get featured case studies
|
||||
getFeaturedCaseStudies: async (): Promise<CaseStudy[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/featured/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching featured case studies:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get latest case studies
|
||||
getLatestCaseStudies: async (limit: number = 6): Promise<CaseStudy[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/latest/?limit=${limit}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching latest case studies:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get popular case studies
|
||||
getPopularCaseStudies: async (limit: number = 6): Promise<CaseStudy[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/popular/?limit=${limit}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching popular case studies:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get related case studies for a specific case study
|
||||
getRelatedCaseStudies: async (slug: string): Promise<CaseStudy[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/${slug}/related/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching related case studies for ${slug}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get all categories
|
||||
getCategories: async (): Promise<CaseStudyCategory[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/categories/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching case study categories:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get categories with case studies
|
||||
getCategoriesWithCaseStudies: async (): Promise<CaseStudyCategory[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/categories/with_case_studies/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching categories with case studies:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get all clients
|
||||
getClients: async (): Promise<Client[]> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/clients/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching clients:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get a client by slug
|
||||
getClientBySlug: async (slug: string): Promise<Client> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/clients/${slug}/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching client ${slug}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get case studies for a specific client
|
||||
getClientCaseStudies: async (slug: string): Promise<CaseStudyListResponse> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_CONFIG.BASE_URL}/api/case-studies/clients/${slug}/case_studies/`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching case studies for client ${slug}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default caseStudyService;
|
||||
|
||||
@@ -9,6 +9,9 @@ export const API_CONFIG = {
|
||||
// Django API Base URL
|
||||
BASE_URL: API_BASE_URL,
|
||||
|
||||
// Media files URL (for uploaded images, etc.)
|
||||
MEDIA_URL: `${API_BASE_URL}/media`,
|
||||
|
||||
// API Endpoints
|
||||
ENDPOINTS: {
|
||||
CONTACT: '/api/contact',
|
||||
|
||||
250
gnx-react/lib/hooks/useBlog.ts
Normal file
250
gnx-react/lib/hooks/useBlog.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
blogService,
|
||||
BlogPost,
|
||||
BlogPostListResponse,
|
||||
BlogCategory,
|
||||
BlogTag,
|
||||
BlogAuthor
|
||||
} from '../api/blogService';
|
||||
|
||||
// Hook for fetching all blog posts
|
||||
export const useBlogPosts = (params?: {
|
||||
category?: string;
|
||||
tag?: string;
|
||||
author?: number;
|
||||
search?: string;
|
||||
featured?: boolean;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) => {
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [pagination, setPagination] = useState<{
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
}>({ count: 0, next: null, previous: null });
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPosts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getPosts(params);
|
||||
setPosts(data.results);
|
||||
setPagination({
|
||||
count: data.count,
|
||||
next: data.next,
|
||||
previous: data.previous
|
||||
});
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setPosts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPosts();
|
||||
}, [params?.category, params?.tag, params?.author, params?.search, params?.page, params?.page_size]);
|
||||
|
||||
return { posts, loading, error, pagination, totalCount: pagination.count };
|
||||
};
|
||||
|
||||
// Hook for fetching a single blog post by slug
|
||||
export const useBlogPost = (slug: string | null) => {
|
||||
const [post, setPost] = useState<BlogPost | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchPost = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getPostBySlug(slug);
|
||||
setPost(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setPost(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPost();
|
||||
}, [slug]);
|
||||
|
||||
return { post, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching featured blog posts
|
||||
export const useFeaturedPosts = () => {
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFeaturedPosts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getFeaturedPosts();
|
||||
setPosts(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setPosts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFeaturedPosts();
|
||||
}, []);
|
||||
|
||||
return { posts, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching latest blog posts
|
||||
export const useLatestPosts = (limit: number = 5) => {
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLatestPosts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getLatestPosts(limit);
|
||||
setPosts(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setPosts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLatestPosts();
|
||||
}, [limit]);
|
||||
|
||||
return { posts, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching popular blog posts
|
||||
export const usePopularPosts = (limit: number = 5) => {
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPopularPosts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getPopularPosts(limit);
|
||||
setPosts(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setPosts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPopularPosts();
|
||||
}, [limit]);
|
||||
|
||||
return { posts, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching blog categories
|
||||
export const useBlogCategories = () => {
|
||||
const [categories, setCategories] = useState<BlogCategory[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getCategories();
|
||||
setCategories(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setCategories([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCategories();
|
||||
}, []);
|
||||
|
||||
return { categories, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching blog tags
|
||||
export const useBlogTags = () => {
|
||||
const [tags, setTags] = useState<BlogTag[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTags = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getTags();
|
||||
setTags(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setTags([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchTags();
|
||||
}, []);
|
||||
|
||||
return { tags, loading, error };
|
||||
};
|
||||
|
||||
// Hook for fetching blog authors
|
||||
export const useBlogAuthors = () => {
|
||||
const [authors, setAuthors] = useState<BlogAuthor[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAuthors = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await blogService.getAuthors();
|
||||
setAuthors(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
setAuthors([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAuthors();
|
||||
}, []);
|
||||
|
||||
return { authors, loading, error };
|
||||
};
|
||||
|
||||
351
gnx-react/lib/hooks/useCaseStudy.ts
Normal file
351
gnx-react/lib/hooks/useCaseStudy.ts
Normal file
@@ -0,0 +1,351 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import caseStudyService, {
|
||||
CaseStudy,
|
||||
CaseStudyCategory,
|
||||
Client,
|
||||
CaseStudyListResponse,
|
||||
} from '../api/caseStudyService';
|
||||
|
||||
// Hook to fetch all case studies
|
||||
export const useCaseStudies = (params?: {
|
||||
category?: string;
|
||||
client?: string;
|
||||
search?: string;
|
||||
featured?: boolean;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) => {
|
||||
const [caseStudies, setCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [pagination, setPagination] = useState<{
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getCaseStudies(params);
|
||||
setCaseStudies(data.results);
|
||||
setPagination({
|
||||
count: data.count,
|
||||
next: data.next,
|
||||
previous: data.previous,
|
||||
});
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching case studies:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCaseStudies();
|
||||
}, [
|
||||
params?.category,
|
||||
params?.client,
|
||||
params?.search,
|
||||
params?.featured,
|
||||
params?.ordering,
|
||||
params?.page,
|
||||
params?.page_size,
|
||||
]);
|
||||
|
||||
return { caseStudies, loading, error, pagination };
|
||||
};
|
||||
|
||||
// Hook to fetch a single case study by slug
|
||||
export const useCaseStudy = (slug: string | null) => {
|
||||
const [caseStudy, setCaseStudy] = useState<CaseStudy | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchCaseStudy = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getCaseStudyBySlug(slug);
|
||||
setCaseStudy(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error(`Error fetching case study ${slug}:`, err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCaseStudy();
|
||||
}, [slug]);
|
||||
|
||||
return { caseStudy, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch featured case studies
|
||||
export const useFeaturedCaseStudies = () => {
|
||||
const [featuredCaseStudies, setFeaturedCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFeaturedCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getFeaturedCaseStudies();
|
||||
setFeaturedCaseStudies(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching featured case studies:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFeaturedCaseStudies();
|
||||
}, []);
|
||||
|
||||
return { featuredCaseStudies, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch latest case studies
|
||||
export const useLatestCaseStudies = (limit: number = 6) => {
|
||||
const [latestCaseStudies, setLatestCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLatestCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getLatestCaseStudies(limit);
|
||||
setLatestCaseStudies(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching latest case studies:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLatestCaseStudies();
|
||||
}, [limit]);
|
||||
|
||||
return { latestCaseStudies, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch popular case studies
|
||||
export const usePopularCaseStudies = (limit: number = 6) => {
|
||||
const [popularCaseStudies, setPopularCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPopularCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getPopularCaseStudies(limit);
|
||||
setPopularCaseStudies(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching popular case studies:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPopularCaseStudies();
|
||||
}, [limit]);
|
||||
|
||||
return { popularCaseStudies, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch related case studies
|
||||
export const useRelatedCaseStudies = (slug: string | null) => {
|
||||
const [relatedCaseStudies, setRelatedCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchRelatedCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getRelatedCaseStudies(slug);
|
||||
setRelatedCaseStudies(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error(`Error fetching related case studies for ${slug}:`, err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchRelatedCaseStudies();
|
||||
}, [slug]);
|
||||
|
||||
return { relatedCaseStudies, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch case study categories
|
||||
export const useCaseStudyCategories = () => {
|
||||
const [categories, setCategories] = useState<CaseStudyCategory[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getCategories();
|
||||
setCategories(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching case study categories:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCategories();
|
||||
}, []);
|
||||
|
||||
return { categories, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch categories with case studies
|
||||
export const useCategoriesWithCaseStudies = () => {
|
||||
const [categories, setCategories] = useState<CaseStudyCategory[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getCategoriesWithCaseStudies();
|
||||
setCategories(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching categories with case studies:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCategories();
|
||||
}, []);
|
||||
|
||||
return { categories, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch all clients
|
||||
export const useClients = () => {
|
||||
const [clients, setClients] = useState<Client[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchClients = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getClients();
|
||||
setClients(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error('Error fetching clients:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchClients();
|
||||
}, []);
|
||||
|
||||
return { clients, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch a single client by slug
|
||||
export const useClient = (slug: string | null) => {
|
||||
const [client, setClient] = useState<Client | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchClient = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getClientBySlug(slug);
|
||||
setClient(data);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error(`Error fetching client ${slug}:`, err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchClient();
|
||||
}, [slug]);
|
||||
|
||||
return { client, loading, error };
|
||||
};
|
||||
|
||||
// Hook to fetch case studies for a specific client
|
||||
export const useClientCaseStudies = (slug: string | null) => {
|
||||
const [caseStudies, setCaseStudies] = useState<CaseStudy[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchClientCaseStudies = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await caseStudyService.getClientCaseStudies(slug);
|
||||
setCaseStudies(data.results);
|
||||
} catch (err) {
|
||||
setError(err as Error);
|
||||
console.error(`Error fetching case studies for client ${slug}:`, err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchClientCaseStudies();
|
||||
}, [slug]);
|
||||
|
||||
return { caseStudies, loading, error };
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Image utility functions
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
|
||||
|
||||
export const FALLBACK_IMAGES = {
|
||||
BLOG: '/images/blog/blog-poster.png',
|
||||
CASE_STUDY: '/images/case/poster.png',
|
||||
@@ -19,8 +21,22 @@ export function getValidImageUrl(imageUrl?: string, fallback?: string): string {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
// If it starts with /, it's already a public path
|
||||
// If it starts with /media/, it's a Django media file - prepend API base URL
|
||||
if (imageUrl.startsWith('/media/')) {
|
||||
return `${API_BASE_URL}${imageUrl}`;
|
||||
}
|
||||
|
||||
// If it starts with /images/, it's a local public file
|
||||
if (imageUrl.startsWith('/images/')) {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
// If it starts with /, check if it's a media file
|
||||
if (imageUrl.startsWith('/')) {
|
||||
// If it contains /media/, prepend API base URL
|
||||
if (imageUrl.includes('/media/')) {
|
||||
return `${API_BASE_URL}${imageUrl}`;
|
||||
}
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
@@ -28,6 +44,9 @@ export function getValidImageUrl(imageUrl?: string, fallback?: string): string {
|
||||
return `/${imageUrl}`;
|
||||
}
|
||||
|
||||
// Alias for backward compatibility
|
||||
export const getImageUrl = getValidImageUrl;
|
||||
|
||||
export function getImageAlt(title?: string, fallback: string = 'Image'): string {
|
||||
return title ? `${title} - Image` : fallback;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user