// services/blogService.ts export interface Professor { id: number; firstName?: string; name?: string; } export interface ApiBlog { id: number; title: string; content: string; professors: Professor[]; tags: string[]; posted: boolean; author?: string; createdDate?: string; updatedDate?: string; publishDate?: string; imageUrl?: string; } export interface Blog { id: number; title: string; excerpt: string; tags: string[]; image: string; publishDate: string; readTime: string; content?: string; professors?: Professor[]; } class BlogService { private apiBaseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080'; async getAllBlogs(): Promise { try { const response = await fetch(`${this.apiBaseUrl}/api/posts`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const apiBlogs: ApiBlog[] = await response.json(); return this.transformApiBlogsToBlogs(apiBlogs); } catch (error) { console.error('Error fetching blogs:', error); return this.getFallbackBlogs(); } } async getBlogById(id: number): Promise { try { const response = await fetch(`${this.apiBaseUrl}/api/posts/${id}`); if (!response.ok) { if (response.status === 404) { return null; } throw new Error(`HTTP error! status: ${response.status}`); } const apiBlog: ApiBlog = await response.json(); return this.transformApiBlogToBlog(apiBlog); } catch (error) { console.error(`Error fetching blog ${id}:`, error); return null; } } async getPostedBlogs(): Promise { try { const response = await fetch(`${this.apiBaseUrl}/api/posts/posted`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const apiBlogs: ApiBlog[] = await response.json(); console.log('Raw API response:', JSON.stringify(apiBlogs, null, 2)); return this.transformApiBlogsToBlogs(apiBlogs); } catch (error) { console.error('Error fetching posted blogs:', error); return this.getFallbackBlogs(); } } async getBlogsByTag(tag: string): Promise { try { const response = await fetch(`${this.apiBaseUrl}/api/posts/tag/${encodeURIComponent(tag)}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const apiBlogs: ApiBlog[] = await response.json(); return this.transformApiBlogsToBlogs(apiBlogs); } catch (error) { console.error(`Error fetching blogs by tag ${tag}:`, error); return []; } } async getTagsWithCount(): Promise<{ [key: string]: number }> { try { const response = await fetch(`${this.apiBaseUrl}/api/posts/tags/count`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const tagCounts: { [key: string]: number } = await response.json(); return tagCounts; } catch (error) { console.error('Error fetching tag counts:', error); return {}; } } private transformApiBlogsToBlogs(apiBlogs: ApiBlog[]): Blog[] { return apiBlogs.map(apiBlog => this.transformApiBlogToBlog(apiBlog)); } private transformApiBlogToBlog(apiBlog: ApiBlog): Blog { return { id: apiBlog.id, title: apiBlog.title, excerpt: this.generateExcerpt(apiBlog.content), tags: apiBlog.tags || [], image: this.getImageUrl(apiBlog.imageUrl), publishDate: this.formatDate(apiBlog.publishDate || apiBlog.createdDate || new Date().toISOString()), readTime: this.calculateReadTime(apiBlog.content), content: apiBlog.content, professors: apiBlog.professors }; } private generateExcerpt(content: string, maxLength: number = 150): string { if (!content) return 'No content available.'; const plainText = content.replace(/<[^>]*>/g, ''); if (plainText.length <= maxLength) { return plainText; } const truncated = plainText.substr(0, maxLength); const lastSpaceIndex = truncated.lastIndexOf(' '); if (lastSpaceIndex > 0) { return truncated.substr(0, lastSpaceIndex) + '...'; } return truncated + '...'; } private calculateReadTime(content: string): string { if (!content) return '1 min read'; const plainText = content.replace(/<[^>]*>/g, ''); const wordCount = plainText.split(/\s+/).filter(word => word.length > 0).length; const readingSpeed = 225; const minutes = Math.ceil(wordCount / readingSpeed); return `${minutes} min read`; } private formatDate(dateString: string): string { try { const date = new Date(dateString); return date.toISOString().split('T')[0]; } catch (error) { return new Date().toISOString().split('T')[0]; } } private getImageUrl(imageUrl?: string): string { // Debug logging console.log('Processing imageUrl:', imageUrl); // Return default if no imageUrl provided if (!imageUrl || imageUrl.trim() === '') { console.log('No imageUrl provided, using default'); return this.getDefaultImage(); } try { // Decode if the URL is encoded const decodedUrl = imageUrl.includes('%') ? decodeURIComponent(imageUrl) : imageUrl; console.log('Decoded URL:', decodedUrl); // If it's already a complete URL, return as is if (decodedUrl.startsWith('http://') || decodedUrl.startsWith('https://')) { console.log('Complete URL, returning as is'); return decodedUrl; } // If it starts with /, it's a relative path if (decodedUrl.startsWith('/')) { // Check if it's a local public path if (decodedUrl.startsWith('/images/')) { console.log('Local public image'); return decodedUrl; } // It's from the backend API const fullUrl = `${this.apiBaseUrl}${decodedUrl}`; console.log('Backend relative path, constructing:', fullUrl); return fullUrl; } // Otherwise, assume it's a backend path without leading / const fullUrl = `${this.apiBaseUrl}/${decodedUrl}`; console.log('Backend path without leading /, constructing:', fullUrl); return fullUrl; } catch (error) { console.error('Error processing image URL:', imageUrl, error); return this.getDefaultImage(); } } private getDefaultImage(): string { // Use a placeholder image service for now return 'https://images.unsplash.com/photo-1505751172876-fa1923c5c528?w=800&h=600&fit=crop'; } private getFallbackBlogs(): Blog[] { return [ { id: 1, title: "Understanding PTSD: Signs, Symptoms, and Treatment Options", excerpt: "Post-traumatic stress disorder affects millions worldwide. Learn about the key symptoms, triggers, and evidence-based treatment approaches that can help individuals reclaim their lives.", tags: ["PTSD", "Mental Health", "Treatment"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-15", readTime: "8 min read" }, { id: 2, title: "Building Resilience After Childhood Trauma", excerpt: "Discover practical strategies and therapeutic approaches for healing from childhood trauma. Explore how resilience can be developed and nurtured throughout the recovery journey.", tags: ["Childhood Trauma", "Resilience", "Healing"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-12", readTime: "6 min read" }, { id: 3, title: "The Role of Family Support in Trauma Recovery", excerpt: "Family support plays a crucial role in trauma recovery. Learn how loved ones can provide effective support and create a healing environment for trauma survivors.", tags: ["Family Support", "Recovery", "Relationships"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-10", readTime: "5 min read" }, { id: 4, title: "Trauma-Informed Care: A Comprehensive Approach", excerpt: "Explore the principles of trauma-informed care and how healthcare providers can create safe, supportive environments for trauma survivors seeking treatment.", tags: ["Trauma-Informed Care", "Healthcare", "Best Practices"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-08", readTime: "7 min read" }, { id: 5, title: "Mindfulness and Meditation for Trauma Healing", excerpt: "Discover how mindfulness practices and meditation techniques can be powerful tools in trauma recovery, helping to regulate emotions and reduce anxiety.", tags: ["Mindfulness", "Meditation", "Coping Strategies"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-05", readTime: "6 min read" }, { id: 6, title: "Workplace Trauma: Recognition and Response", excerpt: "Understanding workplace trauma and its impact on employees. Learn about creating supportive work environments and implementing effective response strategies.", tags: ["Workplace Trauma", "Employee Support", "Mental Health"], image: "/images/default-blog-image.jpg", publishDate: "2024-01-03", readTime: "9 min read" } ]; } } export const blogService = new BlogService();