278 lines
9.4 KiB
TypeScript
278 lines
9.4 KiB
TypeScript
// 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<Blog[]> {
|
|
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<Blog | null> {
|
|
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<Blog[]> {
|
|
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<Blog[]> {
|
|
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(); |