first commit
This commit is contained in:
256
src/services/blogService.ts
Normal file
256
src/services/blogService.ts
Normal file
@ -0,0 +1,256 @@
|
||||
// services/blogService.ts
|
||||
export interface Professor {
|
||||
id: number;
|
||||
firstName?: string;
|
||||
name?: string; // Fallback for different naming
|
||||
}
|
||||
|
||||
export interface ApiBlog {
|
||||
id: number;
|
||||
title: string;
|
||||
content: string;
|
||||
professors: Professor[];
|
||||
tags: string[];
|
||||
posted: boolean; // From your Angular form
|
||||
author?: string;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
publishDate?: string;
|
||||
imageUrl?: string; // Added for uploaded images
|
||||
}
|
||||
|
||||
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_BASE_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(); // Return fallback data if API fails
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Get only posted blogs for public display
|
||||
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();
|
||||
return this.transformApiBlogsToBlogs(apiBlogs);
|
||||
} catch (error) {
|
||||
console.error('Error fetching posted blogs:', error);
|
||||
return this.getFallbackBlogs();
|
||||
}
|
||||
}
|
||||
|
||||
// Get blogs by tag
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get tag counts
|
||||
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), // Use uploaded image or default
|
||||
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.';
|
||||
|
||||
// Strip HTML tags and get plain text
|
||||
const plainText = content.replace(/<[^>]*>/g, '');
|
||||
|
||||
if (plainText.length <= maxLength) {
|
||||
return plainText;
|
||||
}
|
||||
|
||||
// Find the last complete word within the limit
|
||||
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';
|
||||
|
||||
// Strip HTML and count words
|
||||
const plainText = content.replace(/<[^>]*>/g, '');
|
||||
const wordCount = plainText.split(/\s+/).filter(word => word.length > 0).length;
|
||||
|
||||
// Average reading speed is 200-250 words per minute
|
||||
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]; // Returns YYYY-MM-DD format
|
||||
} catch (error) {
|
||||
return new Date().toISOString().split('T')[0]; // Fallback to current date
|
||||
}
|
||||
}
|
||||
|
||||
private getImageUrl(imageUrl?: string): string {
|
||||
if (imageUrl) {
|
||||
// If the imageUrl is a full URL, return as is
|
||||
if (imageUrl.startsWith('http')) {
|
||||
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
|
||||
return this.getDefaultImage();
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
private getFallbackBlogs(): Blog[] {
|
||||
// Return the original hardcoded blogs as fallback when API is unavailable
|
||||
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", // Use default image for fallback
|
||||
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();
|
||||
206
src/services/careerService.ts
Normal file
206
src/services/careerService.ts
Normal file
@ -0,0 +1,206 @@
|
||||
// services/careerService.ts
|
||||
export interface ApiJob {
|
||||
id: number;
|
||||
title: string;
|
||||
department: string;
|
||||
location: string;
|
||||
type: string;
|
||||
experience: string;
|
||||
salary: string;
|
||||
description: string;
|
||||
requirements: string[];
|
||||
responsibilities: string[];
|
||||
isActive: boolean;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: string;
|
||||
title: string;
|
||||
department: string;
|
||||
location: string;
|
||||
type: string;
|
||||
experience: string;
|
||||
salary: string;
|
||||
description: string;
|
||||
requirements: string[];
|
||||
responsibilities: string[];
|
||||
}
|
||||
|
||||
export interface JobApplicationData {
|
||||
jobId: number;
|
||||
fullName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
experience: string;
|
||||
coverLetter?: string;
|
||||
resumeUrl?: string;
|
||||
}
|
||||
|
||||
class CareerService {
|
||||
private apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
async getActiveJobs(): Promise<Job[]> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/jobs/active`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiJobs: ApiJob[] = await response.json();
|
||||
return this.transformApiJobsToJobs(apiJobs);
|
||||
} catch (error) {
|
||||
console.error('Error fetching jobs:', error);
|
||||
return this.getFallbackJobs(); // Return fallback data if API fails
|
||||
}
|
||||
}
|
||||
|
||||
async getJobById(id: number): Promise<Job | null> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/jobs/${id}`);
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiJob: ApiJob = await response.json();
|
||||
return this.transformApiJobToJob(apiJob);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching job ${id}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async submitApplication(applicationData: JobApplicationData): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/job-applications`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(applicationData),
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error submitting application:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private transformApiJobsToJobs(apiJobs: ApiJob[]): Job[] {
|
||||
return apiJobs.map(apiJob => this.transformApiJobToJob(apiJob));
|
||||
}
|
||||
|
||||
private transformApiJobToJob(apiJob: ApiJob): Job {
|
||||
return {
|
||||
id: apiJob.id.toString(),
|
||||
title: apiJob.title,
|
||||
department: apiJob.department,
|
||||
location: apiJob.location,
|
||||
type: apiJob.type,
|
||||
experience: apiJob.experience,
|
||||
salary: apiJob.salary,
|
||||
description: apiJob.description,
|
||||
requirements: apiJob.requirements || [],
|
||||
responsibilities: apiJob.responsibilities || []
|
||||
};
|
||||
}
|
||||
|
||||
private getFallbackJobs(): Job[] {
|
||||
// Return the original hardcoded jobs as fallback
|
||||
return [
|
||||
{
|
||||
id: 'trauma-registrar',
|
||||
title: 'Trauma Registrar',
|
||||
department: 'Trauma Surgery',
|
||||
location: 'Vellore, India',
|
||||
type: 'Rotational',
|
||||
experience: 'MBBS + MS preferred',
|
||||
salary: 'As per hospital norms',
|
||||
description: 'Join our trauma surgery department as a registrar. Open year-round position with rotational duties in emergency trauma care.',
|
||||
requirements: [
|
||||
'MBBS degree required',
|
||||
'MS qualification preferred',
|
||||
'Experience in emergency medicine',
|
||||
'Ability to work in high-pressure environments'
|
||||
],
|
||||
responsibilities: [
|
||||
'Manage trauma cases in emergency situations',
|
||||
'Assist in trauma surgeries',
|
||||
'Participate in rotational duties',
|
||||
'Maintain patient records and documentation'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'trauma-nurse-coordinator',
|
||||
title: 'Trauma Nurse Coordinator',
|
||||
department: 'Trauma Surgery',
|
||||
location: 'Vellore, India',
|
||||
type: 'Full-time',
|
||||
experience: 'BSc Nursing + ATCN or 2 years ICU',
|
||||
salary: 'As per hospital norms',
|
||||
description: 'Coordinate trauma nursing activities and ensure quality patient care in our trauma unit.',
|
||||
requirements: [
|
||||
'BSc Nursing degree required',
|
||||
'ATCN certification OR 2 years ICU experience',
|
||||
'Strong coordination and leadership skills',
|
||||
'Knowledge of trauma protocols'
|
||||
],
|
||||
responsibilities: [
|
||||
'Coordinate trauma nursing activities',
|
||||
'Ensure quality patient care standards',
|
||||
'Train and supervise nursing staff',
|
||||
'Maintain trauma unit protocols'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'clinical-research-fellow',
|
||||
title: 'Clinical Research Fellow',
|
||||
department: 'Trauma Research',
|
||||
location: 'Vellore, India',
|
||||
type: 'Contract (1 year)',
|
||||
experience: 'Research background preferred',
|
||||
salary: 'Fellowship stipend',
|
||||
description: '1-year contract position for trauma study work. Ideal for those interested in clinical research in trauma medicine.',
|
||||
requirements: [
|
||||
'Medical degree or related field',
|
||||
'Interest in clinical research',
|
||||
'Data analysis skills',
|
||||
'Good written and verbal communication'
|
||||
],
|
||||
responsibilities: [
|
||||
'Conduct trauma-related clinical studies',
|
||||
'Collect and analyze research data',
|
||||
'Prepare research reports and publications',
|
||||
'Collaborate with research team'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'short-term-observer',
|
||||
title: 'Short-Term Observer',
|
||||
department: 'Trauma Surgery',
|
||||
location: 'Vellore, India',
|
||||
type: 'Observership (4-8 weeks)',
|
||||
experience: 'Junior residents',
|
||||
salary: 'Observership program',
|
||||
description: '4-8 week observership slots for junior residents. Apply 2 months ahead for this learning opportunity.',
|
||||
requirements: [
|
||||
'Junior resident status',
|
||||
'Interest in trauma surgery',
|
||||
'Apply 2 months in advance',
|
||||
'Valid medical credentials'
|
||||
],
|
||||
responsibilities: [
|
||||
'Observe trauma surgery procedures',
|
||||
'Learn trauma management protocols',
|
||||
'Attend clinical rounds and discussions',
|
||||
'Prepare observership reports'
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export const careerService = new CareerService();
|
||||
254
src/services/educationService.ts
Normal file
254
src/services/educationService.ts
Normal file
@ -0,0 +1,254 @@
|
||||
// services/educationService.ts
|
||||
export interface ApiCourse {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
duration: string;
|
||||
seats: number;
|
||||
category: string;
|
||||
level: string;
|
||||
instructor: string;
|
||||
price?: string;
|
||||
startDate?: string;
|
||||
imageUrl?: string;
|
||||
eligibility: string[];
|
||||
objectives: string[];
|
||||
isActive: boolean;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
}
|
||||
|
||||
export interface Course {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
duration: string;
|
||||
seats: number;
|
||||
category: string;
|
||||
level: string;
|
||||
instructor: string;
|
||||
price: string;
|
||||
startDate: string;
|
||||
image: string;
|
||||
eligibility: string[];
|
||||
objectives: string[];
|
||||
}
|
||||
|
||||
export interface CourseApplicationData {
|
||||
courseId: number;
|
||||
fullName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
qualification: string;
|
||||
experience?: string;
|
||||
coverLetter?: string;
|
||||
resumeUrl?: string;
|
||||
}
|
||||
|
||||
class EducationService {
|
||||
private apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
async getActiveCourses(): Promise<Course[]> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/courses/active`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiCourses: ApiCourse[] = await response.json();
|
||||
return this.transformApiCoursesToCourses(apiCourses);
|
||||
} catch (error) {
|
||||
console.error('Error fetching courses:', error);
|
||||
return this.getFallbackCourses(); // Return fallback data if API fails
|
||||
}
|
||||
}
|
||||
|
||||
async getCourseById(id: number): Promise<Course | null> {
|
||||
try {
|
||||
const token = localStorage.getItem('authToken'); // Or from cookies/session
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/courses/${id}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) return null;
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const apiCourse: ApiCourse = await response.json();
|
||||
return this.transformApiCourseToCourse(apiCourse);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching course ${id}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async submitApplication(applicationData: CourseApplicationData): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/course-applications`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(applicationData),
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error submitting application:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private transformApiCoursesToCourses(apiCourses: ApiCourse[]): Course[] {
|
||||
return apiCourses.map(apiCourse => this.transformApiCourseToCourse(apiCourse));
|
||||
}
|
||||
|
||||
private transformApiCourseToCourse(apiCourse: ApiCourse): Course {
|
||||
return {
|
||||
id: apiCourse.id.toString(),
|
||||
title: apiCourse.title,
|
||||
description: apiCourse.description,
|
||||
duration: apiCourse.duration,
|
||||
seats: apiCourse.seats,
|
||||
category: apiCourse.category,
|
||||
level: apiCourse.level,
|
||||
instructor: apiCourse.instructor,
|
||||
price: apiCourse.price || 'N/A',
|
||||
startDate: apiCourse.startDate || '',
|
||||
image: this.getImageUrl(apiCourse.imageUrl),
|
||||
eligibility: apiCourse.eligibility || [],
|
||||
objectives: apiCourse.objectives || []
|
||||
};
|
||||
}
|
||||
|
||||
private getImageUrl(imageUrl?: string): string {
|
||||
if (imageUrl) {
|
||||
// If imageUrl starts with /uploads/, prepend the full API path
|
||||
if (imageUrl.startsWith('/uploads/')) {
|
||||
return `${this.apiBaseUrl}/api/files${imageUrl}`; // This adds /api/files before /uploads/
|
||||
}
|
||||
// If it's already a full URL, return as is
|
||||
if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
|
||||
return imageUrl;
|
||||
}
|
||||
// Otherwise, assume it's a relative path and prepend base URL with API path
|
||||
return `${this.apiBaseUrl}/api/files/${imageUrl}`;
|
||||
}
|
||||
// Return random default image if no imageUrl provided
|
||||
return this.getRandomDefaultImage();
|
||||
}
|
||||
|
||||
private getRandomDefaultImage(): string {
|
||||
const defaultImages = [
|
||||
"https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=400&h=200&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1559757175-0eb30cd8c063?w=400&h=300&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=300&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=400&h=300&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=400&h=300&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=300&fit=crop&crop=center"
|
||||
];
|
||||
return defaultImages[Math.floor(Math.random() * defaultImages.length)];
|
||||
}
|
||||
|
||||
private getFallbackCourses(): Course[] {
|
||||
// Return the original hardcoded courses as fallback
|
||||
return [
|
||||
{
|
||||
id: '1',
|
||||
title: "ATLS® (Advanced Trauma Life Support)",
|
||||
description: "Eligibility: MBBS + internship complete. Last Course: Aug 31 – Sep 2, 2023 (60 doctors certified). Next Schedule: [#Incomplete – Date TBD]",
|
||||
duration: "3 Days",
|
||||
seats: 60,
|
||||
category: "Certification",
|
||||
level: "Professional",
|
||||
image: "https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=400&h=200&fit=crop&crop=center",
|
||||
instructor: "Trauma Faculty Team",
|
||||
price: "N/A",
|
||||
startDate: "2023-08-31",
|
||||
eligibility: ["MBBS + internship complete"],
|
||||
objectives: ["Advanced trauma life support skills", "Emergency trauma management"]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: "ATCN® (Advanced Trauma Care for Nurses)",
|
||||
description: "First Course: Apr 11-13, 2024 (manikin-based training). Participants: 40 critical care nurses from CMC and partner hospitals. Next Batch: [#Incomplete – Date TBD]",
|
||||
duration: "3 Days",
|
||||
seats: 40,
|
||||
category: "Training",
|
||||
level: "Professional",
|
||||
image: "https://images.unsplash.com/photo-1559757175-0eb30cd8c063?w=400&h=300&fit=crop&crop=center",
|
||||
instructor: "Nursing Faculty Team",
|
||||
price: "N/A",
|
||||
startDate: "2024-04-11",
|
||||
eligibility: ["Registered Nurse", "Critical care experience preferred"],
|
||||
objectives: ["Advanced trauma nursing skills", "Manikin-based training proficiency"]
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: "Trauma First Responder Program",
|
||||
description: "Partners: RCPSG Hope Foundation, local colleges. Locations: Walajapet, Auxilium College—250 students trained. Curriculum: CPR, airway support, bleeding control, scene assessment.",
|
||||
duration: "Varies",
|
||||
seats: 250,
|
||||
category: "Workshop",
|
||||
level: "Beginner",
|
||||
image: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=300&fit=crop&crop=center",
|
||||
instructor: "Community Trainers",
|
||||
price: "N/A",
|
||||
startDate: "2023-01-01",
|
||||
eligibility: ["Students", "Community members"],
|
||||
objectives: ["CPR proficiency", "Basic trauma response", "Scene safety assessment"]
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: "FNB in Trauma Surgery",
|
||||
description: "3-year structured training program in acute surgery, ICU management, and research. Open to MS-qualified surgeons seeking specialized trauma surgery expertise.",
|
||||
duration: "3 Years",
|
||||
seats: 8,
|
||||
category: "Certification",
|
||||
level: "Advanced",
|
||||
image: "https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=400&h=300&fit=crop&crop=center",
|
||||
instructor: "Senior Trauma Surgeons",
|
||||
price: "N/A",
|
||||
startDate: "2025-07-01",
|
||||
eligibility: ["MS qualification in Surgery", "Valid medical license"],
|
||||
objectives: ["Advanced trauma surgery skills", "ICU management", "Research methodology"]
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: "Observerships & Electives",
|
||||
description: "4-8 week clinical blocks for national and international residents. Includes ATLS® course access and hands-on trauma experience. Application by email required.",
|
||||
duration: "4-8 Weeks",
|
||||
seats: 20,
|
||||
category: "Training",
|
||||
level: "Intermediate",
|
||||
image: "https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=400&h=300&fit=crop&crop=center",
|
||||
instructor: "Clinical Faculty",
|
||||
price: "N/A",
|
||||
startDate: "2025-01-15",
|
||||
eligibility: ["Medical residency status", "Valid medical credentials"],
|
||||
objectives: ["Clinical observation skills", "Hands-on trauma experience", "ATLS certification"]
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: "Nursing Skills Lab",
|
||||
description: "Trauma-focused skills laboratory sessions open quarterly (Q2, Q4). Includes chest tube insertion, airway management drills, and EFAST simulation training.",
|
||||
duration: "2 Days",
|
||||
seats: 30,
|
||||
category: "Workshop",
|
||||
level: "Intermediate",
|
||||
image: "https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=300&fit=crop&crop=center",
|
||||
instructor: "Nursing Skills Faculty",
|
||||
price: "N/A",
|
||||
startDate: "2025-04-01",
|
||||
eligibility: ["Licensed nurse", "Basic trauma knowledge"],
|
||||
objectives: ["Chest tube insertion", "Airway management", "EFAST simulation"]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export const educationService = new EducationService();
|
||||
160
src/services/eventService.ts
Normal file
160
src/services/eventService.ts
Normal file
@ -0,0 +1,160 @@
|
||||
// services/eventService.ts
|
||||
export interface Event {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
subtitle: string;
|
||||
mainImage: string;
|
||||
galleryImages: string[];
|
||||
price?: number;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
export interface ApiEvent {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
subtitle?: string;
|
||||
imageUrl?: string;
|
||||
galleryImages?: string[];
|
||||
price?: number;
|
||||
location?: string;
|
||||
// Add other fields that your Spring Boot API returns
|
||||
}
|
||||
|
||||
class EventService {
|
||||
private apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
async getAllEvents(): Promise<Event[]> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/events`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiEvents: ApiEvent[] = await response.json();
|
||||
return this.transformApiEventsToEvents(apiEvents);
|
||||
} catch (error) {
|
||||
console.error('Error fetching events:', error);
|
||||
return this.getFallbackEvents(); // Return fallback data if API fails
|
||||
}
|
||||
}
|
||||
|
||||
async getEventById(id: number): Promise<Event | null> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/events/${id}`);
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiEvent: ApiEvent = await response.json();
|
||||
return this.transformApiEventToEvent(apiEvent);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching event ${id}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private transformApiEventsToEvents(apiEvents: ApiEvent[]): Event[] {
|
||||
return apiEvents.map(apiEvent => this.transformApiEventToEvent(apiEvent));
|
||||
}
|
||||
|
||||
private transformApiEventToEvent(apiEvent: ApiEvent): Event {
|
||||
return {
|
||||
id: apiEvent.id,
|
||||
title: apiEvent.title,
|
||||
description: apiEvent.description,
|
||||
date: this.formatDate(apiEvent.date),
|
||||
subtitle: apiEvent.subtitle || 'More details coming soon',
|
||||
mainImage: apiEvent.imageUrl || this.getDefaultMainImage(),
|
||||
galleryImages: apiEvent.galleryImages || this.getDefaultGalleryImages(),
|
||||
price: apiEvent.price,
|
||||
location: apiEvent.location
|
||||
};
|
||||
}
|
||||
|
||||
private formatDate(dateString: string): string {
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
});
|
||||
} catch (error) {
|
||||
return dateString; // Return original if parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
private getDefaultMainImage(): string {
|
||||
const defaultImages = [
|
||||
'https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=200&fit=crop',
|
||||
'https://images.unsplash.com/photo-1559757175-0eb30cd8c063?w=400&h=200&fit=crop',
|
||||
'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=200&fit=crop',
|
||||
'https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=400&h=200&fit=crop'
|
||||
];
|
||||
return defaultImages[Math.floor(Math.random() * defaultImages.length)];
|
||||
}
|
||||
|
||||
private getDefaultGalleryImages(): string[] {
|
||||
return [
|
||||
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1638202993928-7267aad84c31?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=200&h=100&fit=crop'
|
||||
];
|
||||
}
|
||||
|
||||
private getFallbackEvents(): Event[] {
|
||||
// Return the original hardcoded events as fallback
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
date: '28 September 2025',
|
||||
title: 'Advanced Cardiac Surgery Symposium',
|
||||
description: 'Cutting-edge techniques in minimally invasive cardiac procedures',
|
||||
subtitle: 'Learn from world-renowned cardiac surgeons about the latest innovations',
|
||||
mainImage: 'https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=200&fit=crop',
|
||||
galleryImages: [
|
||||
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1638202993928-7267aad84c31?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2 October 2025',
|
||||
title: 'Pediatric Immunization Update Conference',
|
||||
description: 'Latest developments in childhood vaccination protocols',
|
||||
subtitle: 'Comprehensive review of new vaccine guidelines and safety data',
|
||||
mainImage: 'https://images.unsplash.com/photo-1559757175-0eb30cd8c063?w=400&h=200&fit=crop',
|
||||
galleryImages: [
|
||||
'https://images.unsplash.com/photo-1584362917165-526a968579e8?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1612349317150-e413f6a5b16d?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1581056771107-24ca5f033842?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=200&h=100&fit=crop'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '8 October 2025',
|
||||
title: 'Mental Health in Healthcare Workers',
|
||||
description: 'Addressing burnout and psychological well-being in medical practice',
|
||||
subtitle: 'Strategies for maintaining mental health in high-pressure environments',
|
||||
mainImage: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=200&fit=crop',
|
||||
galleryImages: [
|
||||
'https://images.unsplash.com/photo-1582719508461-905c673771fd?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=200&h=100&fit=crop',
|
||||
'https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=200&h=100&fit=crop'
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export const eventService = new EventService();
|
||||
34
src/services/fileUploadService.ts
Normal file
34
src/services/fileUploadService.ts
Normal file
@ -0,0 +1,34 @@
|
||||
// services/fileUploadService.ts
|
||||
export interface FileUploadResponse {
|
||||
url: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
class FileUploadService {
|
||||
private apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
async uploadFile(file: File): Promise<FileUploadResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/files/upload`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result: FileUploadResponse = await response.json();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('File upload error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const fileUploadService = new FileUploadService();
|
||||
72
src/services/upcomingEventsService.ts
Normal file
72
src/services/upcomingEventsService.ts
Normal file
@ -0,0 +1,72 @@
|
||||
// services/upcomingEventsService.ts
|
||||
export interface ApiUpcomingEvent {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
schedule: string;
|
||||
eventDate?: string;
|
||||
isActive: boolean;
|
||||
createdDate?: string;
|
||||
updatedDate?: string;
|
||||
}
|
||||
|
||||
export interface UpcomingEvent {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
schedule: string;
|
||||
eventDate?: string;
|
||||
}
|
||||
|
||||
class UpcomingEventsService {
|
||||
private apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
async getActiveUpcomingEvents(): Promise<UpcomingEvent[]> {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/api/upcoming-events/active`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const apiEvents: ApiUpcomingEvent[] = await response.json();
|
||||
return this.transformApiEventsToEvents(apiEvents);
|
||||
} catch (error) {
|
||||
console.error('Error fetching upcoming events:', error);
|
||||
return this.getFallbackEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private transformApiEventsToEvents(apiEvents: ApiUpcomingEvent[]): UpcomingEvent[] {
|
||||
return apiEvents.map(apiEvent => ({
|
||||
id: apiEvent.id.toString(),
|
||||
title: apiEvent.title,
|
||||
description: apiEvent.description,
|
||||
schedule: apiEvent.schedule,
|
||||
eventDate: apiEvent.eventDate
|
||||
}));
|
||||
}
|
||||
|
||||
private getFallbackEvents(): UpcomingEvent[] {
|
||||
return [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Simulation-based Team Drills',
|
||||
description: 'Hands-on simulation training designed to improve team coordination and emergency response in high-pressure trauma situations.',
|
||||
schedule: 'Q3 2025'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Online Webinar Series',
|
||||
description: 'Monthly online sessions covering trauma ethics, young doctor support, and professional development in emergency medicine.',
|
||||
schedule: 'Monthly Sessions'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Community Education',
|
||||
description: 'Road safety fairs and school education sessions to promote trauma prevention and basic first aid awareness in the community.',
|
||||
schedule: 'Ongoing'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export const upcomingEventsService = new UpcomingEventsService();
|
||||
Reference in New Issue
Block a user