104 lines
2.9 KiB
React
104 lines
2.9 KiB
React
![]() |
import { useState, useEffect } from 'react'
|
||
|
import axios from 'axios'
|
||
|
import './Projects.css'
|
||
|
|
||
|
function ProjectCard({ project }) {
|
||
|
return (
|
||
|
<div className={`project-card ${project.featured ? 'featured' : ''}`}>
|
||
|
{project.image && (
|
||
|
<div className="project-image">
|
||
|
<img src={`/projects/${project.image}`} alt={project.title} />
|
||
|
</div>
|
||
|
)}
|
||
|
<div className="project-content">
|
||
|
<h3>{project.title}</h3>
|
||
|
<p>{project.description}</p>
|
||
|
<div className="project-tech">
|
||
|
{project.technologies.map(tech => (
|
||
|
<span key={tech} className="tech-tag">{tech}</span>
|
||
|
))}
|
||
|
</div>
|
||
|
<div className="project-links">
|
||
|
{project.githubUrl && (
|
||
|
<a
|
||
|
href={project.githubUrl}
|
||
|
target="_blank"
|
||
|
rel="noopener noreferrer"
|
||
|
className="project-link github"
|
||
|
>
|
||
|
GitHub Repo
|
||
|
</a>
|
||
|
)}
|
||
|
{project.liveUrl && (
|
||
|
<a
|
||
|
href={project.liveUrl}
|
||
|
target="_blank"
|
||
|
rel="noopener noreferrer"
|
||
|
className="project-link live"
|
||
|
>
|
||
|
Live Demo
|
||
|
</a>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
}
|
||
|
|
||
|
function Projects() {
|
||
|
const [projects, setProjects] = useState([])
|
||
|
const [loading, setLoading] = useState(true)
|
||
|
const [error, setError] = useState(null)
|
||
|
|
||
|
useEffect(() => {
|
||
|
const fetchProjects = async () => {
|
||
|
try {
|
||
|
const response = await axios.get('http://localhost:5000/api/projects')
|
||
|
setProjects(response.data)
|
||
|
setLoading(false)
|
||
|
} catch (err) {
|
||
|
console.error('Error fetching projects:', err)
|
||
|
setError('Failed to load projects. Please try again later.')
|
||
|
setLoading(false)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fetchProjects()
|
||
|
}, [])
|
||
|
|
||
|
// Sort projects to show featured ones first
|
||
|
const sortedProjects = [...projects].sort((a, b) => {
|
||
|
if (a.featured && !b.featured) return -1
|
||
|
if (!a.featured && b.featured) return 1
|
||
|
return 0
|
||
|
})
|
||
|
|
||
|
if (loading) return <div className="loading">Loading projects...</div>
|
||
|
if (error) return <div className="error">{error}</div>
|
||
|
|
||
|
return (
|
||
|
<div className="projects-page">
|
||
|
<div className="container">
|
||
|
<h1>Projects</h1>
|
||
|
<p className="projects-intro">
|
||
|
Here are some of the projects I've worked on. Check out my
|
||
|
<a href="https://github.com/yourusername" target="_blank" rel="noopener noreferrer">
|
||
|
GitHub profile
|
||
|
</a> for more.
|
||
|
</p>
|
||
|
|
||
|
<div className="projects-grid">
|
||
|
{sortedProjects.length === 0 ? (
|
||
|
<p>No projects found.</p>
|
||
|
) : (
|
||
|
sortedProjects.map(project => (
|
||
|
<ProjectCard key={project.id} project={project} />
|
||
|
))
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
}
|
||
|
|
||
|
export default Projects
|