Skip to content

Commit f35a023

Browse files
committed
projects.
1 parent 8b24e26 commit f35a023

File tree

8 files changed

+155
-29
lines changed

8 files changed

+155
-29
lines changed

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"front-matter": "^4.0.2",
1212
"react": "^19.2.0",
1313
"react-dom": "^19.2.0",
14+
"react-icons": "^5.5.0",
1415
"react-markdown": "^10.1.0",
1516
"react-router-dom": "^7.9.4",
1617
"react-scripts": "5.0.1",

public/data/Projects.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# My Projects
2+
3+
## Project One
4+
Pinned: true
5+
6+
A short description of the first project.
7+
[Link to Project One](https://example.com/project-one)
8+
![Project One Image](/images/project-one.png)
9+
10+
## Project Two
11+
Pinned: true
12+
13+
A short description of the second project.
14+
[Link to Project Two](https://example.com/project-two)
15+
![Project Two Image](/images/project-two.png)
16+
17+
## Project Three
18+
19+
A short description of the third project.
20+
[Link to Project Three](https://example.com/project-three)
21+

src/data/projects.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/pages/HomePage.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import React, { useState, useEffect } from 'react';
22
import PostItem from '../components/PostItem';
33
import ProjectCard from '../components/ProjectCard';
4-
import projects from '../data/projects';
4+
import { useProjects } from '../utils/projectParser';
5+
import { FaThumbtack, FaBlog } from 'react-icons/fa';
56

67
const HomePage = () => {
78
const [posts, setPosts] = useState([]);
9+
const { projects, loading, error } = useProjects();
810
const pinnedProjects = projects.filter(p => p.pinned);
911

1012
useEffect(() => {
@@ -13,6 +15,14 @@ const HomePage = () => {
1315
setPosts(postSlugs);
1416
}, []);
1517

18+
if (loading) {
19+
return <div className="py-16 sm:py-24 text-center text-white">Loading projects...</div>;
20+
}
21+
22+
if (error) {
23+
return <div className="py-16 sm:py-24 text-center text-red-500">Error loading projects: {error.message}</div>;
24+
}
25+
1626
return (
1727
<div className="py-16 sm:py-24">
1828
<div className="mx-auto max-w-7xl px-6 lg:px-8">
@@ -26,7 +36,9 @@ const HomePage = () => {
2636
</div>
2737

2838
<div className="mt-16">
29-
<h2 className="text-2xl font-semibold tracking-tight text-white text-center">Pinned Projects</h2>
39+
<h2 className="text-2xl font-semibold tracking-tight text-white text-center flex items-center justify-center gap-2">
40+
<FaThumbtack className="text-primary-400" /> Pinned Projects
41+
</h2>
3042
<div className="mt-8 grid grid-cols-1 md:grid-cols-2 gap-8">
3143
{pinnedProjects.map(project => (
3244
<ProjectCard key={project.slug} project={project} />
@@ -35,7 +47,9 @@ const HomePage = () => {
3547
</div>
3648

3749
<div className="mt-8">
38-
<h2 className="text-2xl font-semibold tracking-tight text-white text-center">Recent Blog Posts</h2>
50+
<h2 className="text-2xl font-semibold tracking-tight text-white text-center flex items-center justify-center gap-2">
51+
<FaBlog className="text-primary-400" /> Recent Blog Posts
52+
</h2>
3953
<div className="mt-8">
4054
{posts.slice(0, 5).map(slug => (
4155
<PostItem key={slug} slug={slug} />

src/pages/ProjectPage.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
11
import React from 'react';
22
import { useParams } from 'react-router-dom';
3-
import projects from '../data/projects';
3+
import ReactMarkdown from 'react-markdown';
4+
import { useProjects } from '../utils/projectParser';
45

56
const ProjectPage = () => {
67
const { slug } = useParams();
8+
const { projects, loading, error } = useProjects();
9+
10+
if (loading) {
11+
return <div className="py-16 sm:py-24 text-center text-white">Loading project...</div>;
12+
}
13+
14+
if (error) {
15+
return <div className="py-16 sm:py-24 text-center text-red-500">Error loading project: {error.message}</div>;
16+
}
17+
718
const project = projects.find(p => p.slug === slug);
819

920
if (!project) {
10-
return <div>Project not found</div>;
21+
return <div className="py-16 sm:py-24 text-center text-white">Project not found</div>;
1122
}
1223

1324
return (
1425
<div className="py-16 sm:py-24">
1526
<div className="mx-auto max-w-3xl px-6 lg:px-8">
1627
<h1 className="text-4xl font-bold tracking-tight text-white sm:text-6xl">{project.title}</h1>
17-
<p className="mt-6 text-lg leading-8 text-gray-300">{project.description}</p>
28+
{project.image && <img src={project.image} alt={project.title} className="mt-8 w-full rounded-lg" />}
29+
{project.link && <p className="mt-4 text-lg leading-8 text-gray-300"><a href={project.link} target="_blank" rel="noopener noreferrer" className="text-indigo-400 hover:text-indigo-300">View Project</a></p>}
30+
<div className="mt-6 text-lg leading-8 text-gray-300 prose prose-invert">
31+
<ReactMarkdown>{project.description}</ReactMarkdown>
32+
</div>
1833
</div>
1934
</div>
2035
);

src/pages/ProjectsPage.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import React from 'react';
2-
import projects from '../data/projects';
32
import ProjectCard from '../components/ProjectCard';
3+
import { useProjects } from '../utils/projectParser';
44

55
const ProjectsPage = () => {
6+
const { projects, loading, error } = useProjects();
7+
8+
if (loading) {
9+
return <div className="py-16 sm:py-24 text-center text-white">Loading projects...</div>;
10+
}
11+
12+
if (error) {
13+
return <div className="py-16 sm:py-24 text-center text-red-500">Error loading projects: {error.message}</div>;
14+
}
15+
616
return (
717
<div className="py-16 sm:py-24">
818
<div className="mx-auto max-w-7xl px-6 lg:px-8">

src/utils/projectParser.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useEffect, useState } from 'react';
2+
3+
const parseProjectsMarkdown = (markdown) => {
4+
const projects = [];
5+
const projectSections = markdown.split('## ').slice(1); // Split by '## ' and remove the first empty element
6+
7+
projectSections.forEach(section => {
8+
const lines = section.split('\n').filter(line => line.trim() !== '');
9+
if (lines.length === 0) return;
10+
11+
const title = lines[0].trim();
12+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-*|-*$/g, '');
13+
14+
let description = [];
15+
let link = '';
16+
let image = '';
17+
let pinned = false;
18+
19+
for (let i = 1; i < lines.length; i++) {
20+
const line = lines[i].trim();
21+
if (line.startsWith('[Link to')) {
22+
const match = line.match(/\[Link to .*?\]\((.*?)\)/);
23+
if (match && match[1]) {
24+
link = match[1];
25+
}
26+
} else if (line.startsWith('![') && line.includes('](')) {
27+
const match = line.match(/!\[.*?\]\((.*?)\)/);
28+
if (match && match[1]) {
29+
image = match[1];
30+
}
31+
} else if (line.startsWith('Pinned:')) {
32+
pinned = line.toLowerCase().includes('true');
33+
} else {
34+
description.push(line);
35+
}
36+
}
37+
38+
projects.push({
39+
slug,
40+
title,
41+
description: description.join('\n'),
42+
link,
43+
image,
44+
pinned,
45+
});
46+
});
47+
48+
return projects;
49+
};
50+
51+
export const useProjects = () => {
52+
const [projects, setProjects] = useState([]);
53+
const [loading, setLoading] = useState(true);
54+
const [error, setError] = useState(null);
55+
56+
useEffect(() => {
57+
const fetchProjects = async () => {
58+
try {
59+
const response = await fetch('/data/Projects.md');
60+
if (!response.ok) {
61+
throw new Error(`HTTP error! status: ${response.status}`);
62+
}
63+
const markdown = await response.text();
64+
const parsedProjects = parseProjectsMarkdown(markdown);
65+
setProjects(parsedProjects);
66+
} catch (e) {
67+
setError(e);
68+
} finally {
69+
setLoading(false);
70+
}
71+
};
72+
73+
fetchProjects();
74+
}, []);
75+
76+
return { projects, loading, error };
77+
};

0 commit comments

Comments
 (0)