blog update

This commit is contained in:
2025-10-10 00:37:00 +05:30
parent 317f3a6667
commit 58ea820e14

View File

@ -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 try {
return `${this.apiBaseUrl}${imageUrl}`; // 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();
} }
// Return default image when no image is uploaded
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"
}, },