feat: about me page
This commit is contained in:
+136
-46
@@ -1,3 +1,10 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from "react";
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Routes,
|
||||
@@ -5,15 +12,22 @@ import {
|
||||
Link,
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import { useState, useContext, useEffect, createContext, useRef } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { BlogViewer } from "./utils/BlogViewer";
|
||||
import { BlogList } from "./utils/BlogList";
|
||||
import { RequireAdmin } from "./utils/RouteGuard";
|
||||
import { AdminPage } from "./utils/AdminPage";
|
||||
import Unauthorized from "./utils/UnauthorizedPage";
|
||||
import {
|
||||
FaGithub,
|
||||
FaGitAlt,
|
||||
FaLinkedin,
|
||||
FaTwitter,
|
||||
FaSun,
|
||||
FaMoon,
|
||||
} from "react-icons/fa";
|
||||
|
||||
// Use Vite environment variable for API base URL
|
||||
// Base API URL from env
|
||||
const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000";
|
||||
|
||||
// Auth Context
|
||||
@@ -59,35 +73,50 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [darkMode, setDarkMode] = useState(true);
|
||||
// Initialize theme from localStorage or default to light
|
||||
const [darkMode, setDarkMode] = useState<boolean>(() => {
|
||||
return localStorage.getItem("theme") === "dark";
|
||||
});
|
||||
|
||||
// Sync dark mode state with <html> class and localStorage
|
||||
useEffect(() => {
|
||||
if (darkMode) {
|
||||
document.documentElement.classList.add("dark");
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
}, [darkMode]);
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<AuthProvider>
|
||||
<div className={darkMode ? "dark" : ""}>
|
||||
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||
<AppBar toggleDarkMode={() => setDarkMode(!darkMode)} />
|
||||
<div className="p-4">
|
||||
<Routes>
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/blog" element={<BlogList />} />
|
||||
<Route path="/blog/:slug" element={<BlogViewer />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
<Route
|
||||
path="/admin"
|
||||
element={
|
||||
<RequireAdmin>
|
||||
<AdminPage />
|
||||
</RequireAdmin>
|
||||
}
|
||||
/>
|
||||
<Route path="/unauthorized" element={<Unauthorized />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/signin" element={<SignIn />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
</Routes>
|
||||
</div>
|
||||
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-300">
|
||||
<AppBar
|
||||
darkMode={darkMode}
|
||||
toggleDarkMode={() => setDarkMode((prev) => !prev)}
|
||||
/>
|
||||
<div className="p-4">
|
||||
<Routes>
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/blog" element={<BlogList />} />
|
||||
<Route path="/blog/:slug" element={<BlogViewer />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
<Route
|
||||
path="/admin"
|
||||
element={
|
||||
<RequireAdmin>
|
||||
<AdminPage />
|
||||
</RequireAdmin>
|
||||
}
|
||||
/>
|
||||
<Route path="/unauthorized" element={<Unauthorized />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/signin" element={<SignIn />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</AuthProvider>
|
||||
@@ -95,7 +124,13 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
function AppBar({ toggleDarkMode }: { toggleDarkMode: () => void }) {
|
||||
function AppBar({
|
||||
darkMode,
|
||||
toggleDarkMode,
|
||||
}: {
|
||||
darkMode: boolean;
|
||||
toggleDarkMode: () => void;
|
||||
}) {
|
||||
const { isAuthenticated, logout } = useAuth();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
@@ -111,9 +146,7 @@ function AppBar({ toggleDarkMode }: { toggleDarkMode: () => void }) {
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, [menuOpen]);
|
||||
|
||||
return (
|
||||
@@ -159,7 +192,7 @@ function AppBar({ toggleDarkMode }: { toggleDarkMode: () => void }) {
|
||||
<div className="relative" ref={menuRef}>
|
||||
<button
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
className="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center"
|
||||
className="w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center"
|
||||
>
|
||||
<span className="text-gray-700 dark:text-gray-200">U</span>
|
||||
</button>
|
||||
@@ -183,15 +216,16 @@ function AppBar({ toggleDarkMode }: { toggleDarkMode: () => void }) {
|
||||
)}
|
||||
<button
|
||||
onClick={toggleDarkMode}
|
||||
className="text-sm bg-gray-200 dark:bg-gray-700 px-2 py-1 rounded"
|
||||
className="text-xl p-1 bg-gray-200 dark:bg-gray-700 rounded-full"
|
||||
>
|
||||
Toggle Dark Mode
|
||||
{darkMode ? <FaMoon /> : <FaSun />}
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// Pages and forms
|
||||
function LandingPage() {
|
||||
return (
|
||||
<div className="text-center py-10">
|
||||
@@ -203,20 +237,67 @@ function LandingPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// Profile placeholder
|
||||
function Profile() {
|
||||
return (
|
||||
<div className="text-center py-10">
|
||||
<h2 className="text-2xl font-bold mb-4">Profile</h2>
|
||||
<p>Profile page coming soon.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function About() {
|
||||
return (
|
||||
<div className="text-center py-10">
|
||||
<h2 className="text-2xl font-bold mb-4">About Me</h2>
|
||||
<p>About page content coming soon.</p>
|
||||
<div className="py-10">
|
||||
<div className="flex justify-center items-end space-x-4">
|
||||
<img
|
||||
src="/images/photo1.jpg"
|
||||
alt="Photo 1"
|
||||
className="w-24 h-24 rounded-full"
|
||||
/>
|
||||
<img
|
||||
src="/images/photo2.jpg"
|
||||
alt="Photo 2"
|
||||
className="w-24 h-24 rounded-full transform translate-y-4"
|
||||
/>
|
||||
<img
|
||||
src="/images/photo3.jpg"
|
||||
alt="Photo 3"
|
||||
className="w-24 h-24 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-8 px-4">
|
||||
<h2 className="text-3xl font-bold mb-4">About Me</h2>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
I am a software engineer at Whisker who designs and implements
|
||||
whatever is highest priority. I work with every team in the
|
||||
engineering organization to coordinate mission-critical projects
|
||||
across backend, mobile, and firmware. I’m comfortable in many tech
|
||||
stacks and learn new ones quickly—never afraid to jump in the deep end
|
||||
and adapt on the fly.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 flex flex-col items-center space-y-2">
|
||||
<a
|
||||
href="https://github.com/amuszyn"
|
||||
className="flex items-center space-x-2 text-blue-600 hover:underline"
|
||||
>
|
||||
<FaGithub />
|
||||
<span>github.com/amuszyn</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://gitea.muszyn.dev/"
|
||||
className="flex items-center space-x-2 text-blue-600 hover:underline"
|
||||
>
|
||||
<FaGitAlt />
|
||||
<span>gitea.muszyn.dev/</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/almuszynski/"
|
||||
className="flex items-center space-x-2 text-blue-600 hover:underline"
|
||||
>
|
||||
<FaLinkedin />
|
||||
<span>linkedin.com/in/almuszynski</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://x.com/Muszynlol"
|
||||
className="flex items-center space-x-2 text-blue-600 hover:underline"
|
||||
>
|
||||
<FaTwitter />
|
||||
<span>x.com/Muszynlol</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -230,6 +311,15 @@ function Contact() {
|
||||
);
|
||||
}
|
||||
|
||||
function Profile() {
|
||||
return (
|
||||
<div className="text-center py-10">
|
||||
<h2 className="text-2xl font-bold mb-4">Profile</h2>
|
||||
<p>Profile page coming soon.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Registration form with API integration
|
||||
interface RegisterForm {
|
||||
username: string;
|
||||
|
||||
Reference in New Issue
Block a user