Skip to content

Commit 5d1ca90

Browse files
committed
feat: ✨ Implement dynamic fetching of GitHub projects
- Updated the Works and Projects components to fetch the latest GitHub projects dynamically using the GitHub API. - Added loading and error states for better user experience. - Enhanced project display with last updated timestamps and improved organization of projects by year and month. - Refactored ProjectCard to accommodate new data structure and display additional project details.
1 parent 6b4a3b5 commit 5d1ca90

3 files changed

Lines changed: 322 additions & 90 deletions

File tree

src/components/main/Works.jsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22
import { Tilt } from "react-tilt";
33
import { motion } from "framer-motion";
44
import { useTranslation } from 'react-i18next';
5+
import { useEffect, useState } from 'react';
56

67
import { styles } from "../../styles"; // Importing styles
78
import { github, githubFallback } from "../../assets"; // Importing GitHub icon
89
import { SectionWrapper } from "../../hoc"; // Importing a section wrapper HOC
9-
import generateData from "../../constants"; // Importing data (projects)
1010
import { fadeIn, textVariant } from "../../utils/motion"; // Importing motion-related utilities
1111

1212
// ProjectCard component for displaying individual project cards
1313
const ProjectCard = ({
1414
index,
1515
name,
1616
description,
17-
tags,
18-
image,
19-
imageFallback,
20-
source_code_link,
17+
html_url,
18+
updated_at,
2119
}) => {
2220
return (
2321
<motion.div variants={fadeIn("up", "spring", index * 0.5, 0.75)}>
@@ -33,18 +31,9 @@ const ProjectCard = ({
3331
{/* Container for the project image */}
3432
<div className='relative w-full h-[230px]'>
3533
{/* Project image */}
36-
<img
37-
src={image}
38-
srcSet={`${image} 1x, ${imageFallback} 2x`}
39-
alt='project_image'
40-
className='w-full h-full object-cover rounded-2xl'
41-
loading="lazy"
42-
/>
43-
44-
{/* GitHub icon for the source code link */}
4534
<div className='absolute inset-0 flex justify-end m-3 card-img_hover'>
4635
<div
47-
onClick={() => window.open(source_code_link, "_blank")}
36+
onClick={() => window.open(html_url, "_blank")}
4837
className='black-gradient w-10 h-10 rounded-full flex justify-center items-center cursor-pointer'
4938
>
5039
<img
@@ -61,19 +50,8 @@ const ProjectCard = ({
6150
{/* Project details */}
6251
<div className='mt-5'>
6352
<h1 className='text-textColor font-bold text-[24px]'>{name}</h1>
64-
<p className='mt-2 dark:text-secondary text-textColor text-[14px]'>{description}</p>
65-
</div>
66-
67-
{/* Project tags */}
68-
<div className='mt-4 flex flex-wrap gap-2'>
69-
{tags.map((tag) => (
70-
<p
71-
key={`${name}-${tag.name}`}
72-
className={`text-[14px] ${tag.color}`}
73-
>
74-
#{tag.name}
75-
</p>
76-
))}
53+
<p className='mt-2 dark:text-secondary text-textColor text-[14px]'>{description || 'No description available'}</p>
54+
<p className='mt-2 text-sm text-gray-500'>Last updated: {new Date(updated_at).toLocaleDateString()}</p>
7755
</div>
7856
</Tilt>
7957
</motion.div>
@@ -83,7 +61,34 @@ const ProjectCard = ({
8361
// Works component displaying a section with a list of projects
8462
const Works = () => {
8563
const { t } = useTranslation();
86-
const { projects } = generateData(); // Fetching projects data from constants
64+
const [projects, setProjects] = useState([]);
65+
const [loading, setLoading] = useState(true);
66+
const [error, setError] = useState(null);
67+
68+
useEffect(() => {
69+
const fetchLatestProjects = async () => {
70+
try {
71+
const response = await fetch('https://api.github.com/users/clementbobin/repos?sort=updated&direction=desc&per_page=3', {
72+
headers: {
73+
'Accept': 'application/vnd.github.v3+json'
74+
}
75+
});
76+
77+
if (!response.ok) {
78+
throw new Error(`Failed to fetch GitHub projects: ${response.status}`);
79+
}
80+
81+
const data = await response.json();
82+
setProjects(data);
83+
} catch (err) {
84+
setError(err.message);
85+
} finally {
86+
setLoading(false);
87+
}
88+
};
89+
90+
fetchLatestProjects();
91+
}, []);
8792

8893
return (
8994
<section>
@@ -103,12 +108,17 @@ const Works = () => {
103108
</motion.p>
104109
</div>
105110

106-
{/* Displaying individual project cards */}
107-
<div className='mt-20 flex flex-wrap gap-7'>
108-
{projects.map((project, index) => (
109-
<ProjectCard key={`project-${index}`} index={index} {...project} />
110-
))}
111-
</div>
111+
{loading ? (
112+
<div className="mt-20">Loading latest projects...</div>
113+
) : error ? (
114+
<div className="mt-20 text-red-500">Error: {error}</div>
115+
) : (
116+
<div className='mt-20 flex flex-wrap gap-7'>
117+
{projects.map((project, index) => (
118+
<ProjectCard key={`project-${index}`} index={index} {...project} />
119+
))}
120+
</div>
121+
)}
112122
</section>
113123
);
114124
};

0 commit comments

Comments
 (0)