feat: initial setup, frontend, backend, postgresql
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Routes,
|
||||
Route,
|
||||
Link,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { BlogViewer } from "./utils/BlogViewer";
|
||||
import { BlogList } from "./utils/BlogList";
|
||||
|
||||
function App() {
|
||||
const [darkMode, setDarkMode] = useState(true);
|
||||
|
||||
return (
|
||||
<div className={darkMode ? "dark" : ""}>
|
||||
<Router>
|
||||
<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 />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AppBar({ toggleDarkMode }: { toggleDarkMode: () => void }) {
|
||||
return (
|
||||
<nav className="bg-white dark:bg-gray-800 shadow p-4 flex space-x-4 items-center">
|
||||
<Link to="/" className="font-bold text-blue-600 dark:text-blue-400">
|
||||
Home
|
||||
</Link>
|
||||
<Link
|
||||
to="/blog"
|
||||
className="text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className="text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
About Me
|
||||
</Link>
|
||||
<Link
|
||||
to="/contact"
|
||||
className="text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
Contact
|
||||
</Link>
|
||||
<button
|
||||
onClick={toggleDarkMode}
|
||||
className="ml-auto text-sm bg-gray-200 dark:bg-gray-700 px-2 py-1 rounded"
|
||||
>
|
||||
Toggle Dark Mode
|
||||
</button>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
function LandingPage() {
|
||||
return (
|
||||
<div className="text-center py-10">
|
||||
<h1 className="text-4xl font-bold mb-4">Welcome to My Site</h1>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
Explore my blog, learn about me, or get in touch.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const BlogPost = () => {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const [content, setContent] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) return;
|
||||
|
||||
fetch(`../public/blogs/${slug}.md`)
|
||||
.then((res) => res.text())
|
||||
.then((text) => setContent(text));
|
||||
}, [slug]);
|
||||
|
||||
if (!content) return <div>Loading...</div>;
|
||||
|
||||
return (
|
||||
<article className="prose dark:prose-invert max-w-none p-4">
|
||||
<ReactMarkdown>{content}</ReactMarkdown>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
function About() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">About Me</h2>
|
||||
<p>This is a placeholder for information about me.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Contact() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Contact</h2>
|
||||
<p>This is a placeholder for contact information or a form.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user