blog update
This commit is contained in:
@ -2,7 +2,7 @@
|
|||||||
export interface Professor {
|
export interface Professor {
|
||||||
id: number;
|
id: number;
|
||||||
firstName?: string;
|
firstName?: string;
|
||||||
name?: string; // Fallback for different naming
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiBlog {
|
export interface ApiBlog {
|
||||||
@ -11,12 +11,12 @@ export interface ApiBlog {
|
|||||||
content: string;
|
content: string;
|
||||||
professors: Professor[];
|
professors: Professor[];
|
||||||
tags: string[];
|
tags: string[];
|
||||||
posted: boolean; // From your Angular form
|
posted: boolean;
|
||||||
author?: string;
|
author?: string;
|
||||||
createdDate?: string;
|
createdDate?: string;
|
||||||
updatedDate?: string;
|
updatedDate?: string;
|
||||||
publishDate?: string;
|
publishDate?: string;
|
||||||
imageUrl?: string; // Added for uploaded images
|
imageUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Blog {
|
export interface Blog {
|
||||||
@ -44,7 +44,7 @@ class BlogService {
|
|||||||
return this.transformApiBlogsToBlogs(apiBlogs);
|
return this.transformApiBlogsToBlogs(apiBlogs);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching blogs:', error);
|
console.error('Error fetching blogs:', error);
|
||||||
return this.getFallbackBlogs(); // Return fallback data if API fails
|
return this.getFallbackBlogs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,6 @@ class BlogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get only posted blogs for public display
|
|
||||||
async getPostedBlogs(): Promise<Blog[]> {
|
async getPostedBlogs(): Promise<Blog[]> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBaseUrl}/api/posts/posted`);
|
const response = await fetch(`${this.apiBaseUrl}/api/posts/posted`);
|
||||||
@ -80,7 +79,6 @@ class BlogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get blogs by tag
|
|
||||||
async getBlogsByTag(tag: string): Promise<Blog[]> {
|
async getBlogsByTag(tag: string): Promise<Blog[]> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBaseUrl}/api/posts/tag/${encodeURIComponent(tag)}`);
|
const response = await fetch(`${this.apiBaseUrl}/api/posts/tag/${encodeURIComponent(tag)}`);
|
||||||
@ -95,7 +93,6 @@ class BlogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get tag counts
|
|
||||||
async getTagsWithCount(): Promise<{ [key: string]: number }> {
|
async getTagsWithCount(): Promise<{ [key: string]: number }> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBaseUrl}/api/posts/tags/count`);
|
const response = await fetch(`${this.apiBaseUrl}/api/posts/tags/count`);
|
||||||
@ -120,7 +117,7 @@ class BlogService {
|
|||||||
title: apiBlog.title,
|
title: apiBlog.title,
|
||||||
excerpt: this.generateExcerpt(apiBlog.content),
|
excerpt: this.generateExcerpt(apiBlog.content),
|
||||||
tags: apiBlog.tags || [],
|
tags: apiBlog.tags || [],
|
||||||
image: this.getImageUrl(apiBlog.imageUrl), // Use uploaded image or default
|
image: this.getImageUrl(apiBlog.imageUrl),
|
||||||
publishDate: this.formatDate(apiBlog.publishDate || apiBlog.createdDate || new Date().toISOString()),
|
publishDate: this.formatDate(apiBlog.publishDate || apiBlog.createdDate || new Date().toISOString()),
|
||||||
readTime: this.calculateReadTime(apiBlog.content),
|
readTime: this.calculateReadTime(apiBlog.content),
|
||||||
content: apiBlog.content,
|
content: apiBlog.content,
|
||||||
@ -131,14 +128,12 @@ class BlogService {
|
|||||||
private generateExcerpt(content: string, maxLength: number = 150): string {
|
private generateExcerpt(content: string, maxLength: number = 150): string {
|
||||||
if (!content) return 'No content available.';
|
if (!content) return 'No content available.';
|
||||||
|
|
||||||
// Strip HTML tags and get plain text
|
|
||||||
const plainText = content.replace(/<[^>]*>/g, '');
|
const plainText = content.replace(/<[^>]*>/g, '');
|
||||||
|
|
||||||
if (plainText.length <= maxLength) {
|
if (plainText.length <= maxLength) {
|
||||||
return plainText;
|
return plainText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the last complete word within the limit
|
|
||||||
const truncated = plainText.substr(0, maxLength);
|
const truncated = plainText.substr(0, maxLength);
|
||||||
const lastSpaceIndex = truncated.lastIndexOf(' ');
|
const lastSpaceIndex = truncated.lastIndexOf(' ');
|
||||||
|
|
||||||
@ -152,11 +147,9 @@ class BlogService {
|
|||||||
private calculateReadTime(content: string): string {
|
private calculateReadTime(content: string): string {
|
||||||
if (!content) return '1 min read';
|
if (!content) return '1 min read';
|
||||||
|
|
||||||
// Strip HTML and count words
|
|
||||||
const plainText = content.replace(/<[^>]*>/g, '');
|
const plainText = content.replace(/<[^>]*>/g, '');
|
||||||
const wordCount = plainText.split(/\s+/).filter(word => word.length > 0).length;
|
const wordCount = plainText.split(/\s+/).filter(word => word.length > 0).length;
|
||||||
|
|
||||||
// Average reading speed is 200-250 words per minute
|
|
||||||
const readingSpeed = 225;
|
const readingSpeed = 225;
|
||||||
const minutes = Math.ceil(wordCount / readingSpeed);
|
const minutes = Math.ceil(wordCount / readingSpeed);
|
||||||
|
|
||||||
@ -166,41 +159,57 @@ class BlogService {
|
|||||||
private formatDate(dateString: string): string {
|
private formatDate(dateString: string): string {
|
||||||
try {
|
try {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toISOString().split('T')[0]; // Returns YYYY-MM-DD format
|
return date.toISOString().split('T')[0];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return new Date().toISOString().split('T')[0]; // Fallback to current date
|
return new Date().toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getImageUrl(imageUrl?: string): string {
|
private getImageUrl(imageUrl?: string): string {
|
||||||
if (imageUrl) {
|
// Return default if no imageUrl provided
|
||||||
// If the imageUrl is a full URL, return as is
|
if (!imageUrl || imageUrl.trim() === '') {
|
||||||
if (imageUrl.startsWith('http')) {
|
return this.getDefaultImage();
|
||||||
return imageUrl;
|
|
||||||
}
|
|
||||||
// If it's a relative path from your backend, construct full URL
|
|
||||||
return `${this.apiBaseUrl}${imageUrl}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return default image when no image is uploaded
|
try {
|
||||||
return this.getDefaultImage();
|
// Decode if the URL is encoded
|
||||||
|
const decodedUrl = imageUrl.includes('%') ? decodeURIComponent(imageUrl) : imageUrl;
|
||||||
|
|
||||||
|
// If it's already a complete URL, return as is
|
||||||
|
if (decodedUrl.startsWith('http://') || decodedUrl.startsWith('https://')) {
|
||||||
|
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/')) {
|
||||||
|
return decodedUrl;
|
||||||
|
}
|
||||||
|
// It's from the backend API
|
||||||
|
return `${this.apiBaseUrl}${decodedUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, assume it's a backend path without leading /
|
||||||
|
return `${this.apiBaseUrl}/${decodedUrl}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing image URL:', imageUrl, error);
|
||||||
|
return this.getDefaultImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDefaultImage(): string {
|
private getDefaultImage(): string {
|
||||||
// Return a single default image from your public folder
|
|
||||||
// Make sure to add this image to your Next.js public/images directory
|
|
||||||
return '/images/default-blog-image.jpg';
|
return '/images/default-blog-image.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFallbackBlogs(): Blog[] {
|
private getFallbackBlogs(): Blog[] {
|
||||||
// Return the original hardcoded blogs as fallback when API is unavailable
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "Understanding PTSD: Signs, Symptoms, and Treatment Options",
|
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.",
|
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"],
|
tags: ["PTSD", "Mental Health", "Treatment"],
|
||||||
image: "/images/default-blog-image.jpg", // Use default image for fallback
|
image: "/images/default-blog-image.jpg",
|
||||||
publishDate: "2024-01-15",
|
publishDate: "2024-01-15",
|
||||||
readTime: "8 min read"
|
readTime: "8 min read"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user