diff --git a/client/src/App.jsx b/client/src/App.jsx index f67355a..c7b613f 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,35 +1,29 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' +import { Routes, Route } from 'react-router-dom' +import Header from './components/Header' +import Footer from './components/Footer' +import About from './pages/About' +import Blog from './pages/Blog' +import Post from './pages/Post' +import Projects from './pages/Projects' +import NotFound from './pages/NotFound' import './App.css' function App() { - const [count, setCount] = useState(0) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- +
+
+
+ + } /> + } /> + } /> + } /> + } /> + +
+
) } -export default App +export default App \ No newline at end of file diff --git a/client/src/components/Footer.jsx b/client/src/components/Footer.jsx new file mode 100644 index 0000000..2d338e1 --- /dev/null +++ b/client/src/components/Footer.jsx @@ -0,0 +1,20 @@ +import './Footer.css' + +function Footer() { + const currentYear = new Date().getFullYear() + + return ( + + ) +} + +export default Footer \ No newline at end of file diff --git a/client/src/components/Headers.jsx b/client/src/components/Headers.jsx new file mode 100644 index 0000000..aa37c2f --- /dev/null +++ b/client/src/components/Headers.jsx @@ -0,0 +1,21 @@ +import { NavLink } from 'react-router-dom' +import './Header.css' + +function Header() { + return ( +
+
+

Your Name

+ +
+
+ ) +} + +export default Header \ No newline at end of file diff --git a/client/src/main.jsx b/client/src/main.jsx index b9a1a6d..f4fa3e5 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,10 +1,13 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' +import React from 'react' +import ReactDOM from 'react-dom/client' +import { BrowserRouter } from 'react-router-dom' import App from './App.jsx' +import './index.css' -createRoot(document.getElementById('root')).render( - - - , +ReactDOM.createRoot(document.getElementById('root')).render( + + + + + , ) diff --git a/client/src/pages/About.jsx b/client/src/pages/About.jsx new file mode 100644 index 0000000..4d8ea7b --- /dev/null +++ b/client/src/pages/About.jsx @@ -0,0 +1,44 @@ +import './About.css' + +function About() { + return ( +
+
+

About Me

+
+
+ Your Name +
+
+

+ Hello, I'm [Your Name]. I'm a [Your Profession] based in [Your Location]. + I specialize in [Your Skills/Specialties]. +

+

+ I've worked on various projects, from [Type of Project] to [Type of Project]. + My goal is to [Your Professional Goal]. +

+

+ When I'm not coding, I enjoy [Your Hobbies/Interests]. +

+
+

Skills

+
    +
  • JavaScript (React, Node.js)
  • +
  • HTML & CSS
  • +
  • Python
  • + {/* Add more skills as needed */} +
+
+
+

Contact

+

Feel free to reach out to me at your.email@example.com

+
+
+
+
+
+ ) +} + +export default About \ No newline at end of file diff --git a/client/src/pages/Blog.jsx b/client/src/pages/Blog.jsx new file mode 100644 index 0000000..96bd4c7 --- /dev/null +++ b/client/src/pages/Blog.jsx @@ -0,0 +1,109 @@ +import { useState, useEffect } from 'react' +import { Link } from 'react-router-dom' +import axios from 'axios' +import { format } from 'date-fns' +import './Blog.css' + +function Blog() { + const [posts, setPosts] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [selectedTag, setSelectedTag] = useState(null) + + useEffect(() => { + const fetchPosts = async () => { + try { + const response = await axios.get('http://localhost:5000/api/posts') + setPosts(response.data) + setLoading(false) + } catch (err) { + console.error('Error fetching posts:', err) + setError('Failed to load blog posts. Please try again later.') + setLoading(false) + } + } + + fetchPosts() + }, []) + + // Extract all unique tags from posts + const allTags = posts.reduce((tags, post) => { + if (post.tags) { + post.tags.forEach(tag => { + if (!tags.includes(tag)) { + tags.push(tag) + } + }) + } + return tags + }, []) + + // Filter posts by selected tag + const filteredPosts = selectedTag + ? posts.filter(post => post.tags && post.tags.includes(selectedTag)) + : posts + + if (loading) return
Loading posts...
+ if (error) return
{error}
+ + return ( +
+
+

Blog

+ + {allTags.length > 0 && ( +
+ Filter by tag: + + {allTags.map(tag => ( + + ))} +
+ )} + +
+ {filteredPosts.length === 0 ? ( +

No posts found.

+ ) : ( + filteredPosts.map(post => ( +
+

+ {post.title} +

+
+ + {post.tags && ( +
+ {post.tags.map(tag => ( + setSelectedTag(tag)}> + {tag} + + ))} +
+ )} +
+

{post.excerpt}

+ + Read more → + +
+ )) + )} +
+
+
+ ) +} + +export default Blog \ No newline at end of file diff --git a/client/src/pages/NotFound.jsx b/client/src/pages/NotFound.jsx new file mode 100644 index 0000000..580f909 --- /dev/null +++ b/client/src/pages/NotFound.jsx @@ -0,0 +1,21 @@ +import { Link } from 'react-router-dom' +import './NotFound.css' + +function NotFound() { + return ( +
+
+

404

+

Page Not Found

+

+ Oops! The page you're looking for doesn't exist or has been moved. +

+ + Go back to homepage + +
+
+ ) +} + +export default NotFound \ No newline at end of file diff --git a/client/src/pages/Post.jsx b/client/src/pages/Post.jsx new file mode 100644 index 0000000..3a2d650 --- /dev/null +++ b/client/src/pages/Post.jsx @@ -0,0 +1,72 @@ +import { useState, useEffect } from 'react' +import { useParams, Link, useNavigate } from 'react-router-dom' +import axios from 'axios' +import { format } from 'date-fns' +import './Post.css' + +function Post() { + const { slug } = useParams() + const navigate = useNavigate() + const [post, setPost] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + const fetchPost = async () => { + try { + const response = await axios.get(`http://localhost:5000/api/posts/${slug}`) + setPost(response.data) + setLoading(false) + } catch (err) { + console.error(`Error fetching post with slug ${slug}:`, err) + setError('Post not found or unable to load.') + setLoading(false) + } + } + + fetchPost() + }, [slug]) + + if (loading) return
Loading post...
+ if (error) return
{error}
+ if (!post) return
Post not found
+ + return ( +
+
+
+
+

{post.title}

+
+ + {post.tags && ( +
+ {post.tags.map(tag => ( + + {tag} + + ))} +
+ )} +
+
+ +
+ + +
+
+
+ ) +} + +export default Post \ No newline at end of file diff --git a/client/src/pages/Projects.jsx b/client/src/pages/Projects.jsx new file mode 100644 index 0000000..2d4eeb4 --- /dev/null +++ b/client/src/pages/Projects.jsx @@ -0,0 +1,104 @@ +import { useState, useEffect } from 'react' +import axios from 'axios' +import './Projects.css' + +function ProjectCard({ project }) { + return ( +
+ {project.image && ( +
+ {project.title} +
+ )} +
+

{project.title}

+

{project.description}

+
+ {project.technologies.map(tech => ( + {tech} + ))} +
+
+ {project.githubUrl && ( + + GitHub Repo + + )} + {project.liveUrl && ( + + Live Demo + + )} +
+
+
+ ) +} + +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
Loading projects...
+ if (error) return
{error}
+ + return ( +
+
+

Projects

+

+ Here are some of the projects I've worked on. Check out my + + GitHub profile + for more. +

+ +
+ {sortedProjects.length === 0 ? ( +

No projects found.

+ ) : ( + sortedProjects.map(project => ( + + )) + )} +
+
+
+ ) +} + +export default Projects \ No newline at end of file