feat: about me page

This commit is contained in:
2025-06-14 11:22:00 -04:00
parent 8db73f113c
commit 2c73c3ba4c
5 changed files with 237 additions and 48 deletions
+136 -46
View File
@@ -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. Im comfortable in many tech
stacks and learn new ones quicklynever 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;