navi
This commit is contained in:
16
src/App.css
Normal file
16
src/App.css
Normal file
@@ -0,0 +1,16 @@
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
26
src/components/MainNavigation.css
Normal file
26
src/components/MainNavigation.css
Normal file
@@ -0,0 +1,26 @@
|
||||
/*src/components/MainNavigation.css*/
|
||||
.main-navigation {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.main-navigation ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main-navigation ul li {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.main-navigation ul li a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.main-navigation ul li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
32
src/components/MainNavigation.js
Normal file
32
src/components/MainNavigation.js
Normal file
@@ -0,0 +1,32 @@
|
||||
//src/components/MainNavigation.js
|
||||
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./MainNavigation.css";
|
||||
|
||||
const MainNavigation = () => {
|
||||
return (
|
||||
<nav className="main-navigation">
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/">Home</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/login">Login</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://mail.hangman-lab.top" target="_blank" rel="noopener noreferrer">
|
||||
MailBox
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://git.hangman-lab.top" target="_blank" rel="noopener noreferrer">
|
||||
Git
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainNavigation;
|
||||
0
src/components/MarkdownContent.css
Normal file
0
src/components/MarkdownContent.css
Normal file
34
src/components/MarkdownContent.js
Normal file
34
src/components/MarkdownContent.js
Normal file
@@ -0,0 +1,34 @@
|
||||
//src/components/MarkdownContent.js
|
||||
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {fetchWithCache} from "../utils/fetchWIthCache";
|
||||
|
||||
const MarkdownContent = () => {
|
||||
const { id } = useParams();
|
||||
const [content, setContent] = useState(null);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchWithCache(`/api/markdown/${id}`)
|
||||
.then((data) => setContent(data))
|
||||
.catch((error) => setError(error));
|
||||
}, [id]);
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error}</div>;
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="markdown-content">
|
||||
<pre>{content}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownContent;
|
||||
29
src/components/SideNavigation.css
Normal file
29
src/components/SideNavigation.css
Normal file
@@ -0,0 +1,29 @@
|
||||
/*src/components/SideNavigation.css*/
|
||||
.side-navigation {
|
||||
width: 250px;
|
||||
background-color: #f4f4f4;
|
||||
padding: 1rem;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.side-navigation h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.side-navigation ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.side-navigation ul li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.side-navigation ul li a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.side-navigation ul li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
72
src/components/SideNavigation.js
Normal file
72
src/components/SideNavigation.js
Normal file
@@ -0,0 +1,72 @@
|
||||
// src/components/SideNavigation.js
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./SideNavigation.css";
|
||||
import { fetchWithCache } from "../utils/fetchWithCache";
|
||||
|
||||
const SideNavigation = () => {
|
||||
const [markdowns, setMarkdowns] = useState([]);
|
||||
const [tree, setTree] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchWithCache("/api/markdown/")
|
||||
.then((data) => {
|
||||
setMarkdowns(data);
|
||||
setTree(buildTree(data));
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
}, []);
|
||||
|
||||
function buildTree(markdowns) {
|
||||
const root = {};
|
||||
|
||||
markdowns.forEach((markdown) => {
|
||||
const segments = markdown.path.split("/").filter(Boolean);
|
||||
let current = root;
|
||||
|
||||
segments.forEach((segment, index) => {
|
||||
if (!current[segment]) {
|
||||
current[segment] =
|
||||
index === segments.length - 1 ? { markdown } : {};
|
||||
}
|
||||
current = current[segment];
|
||||
});
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function renderTree(node, basePath = "") {
|
||||
return (
|
||||
<ul>
|
||||
{Object.entries(node).map(([key, value]) => {
|
||||
if (value.markdown) {
|
||||
return (
|
||||
<li key={value.markdown.id}>
|
||||
<Link to={`/markdown/${value.markdown.id}`}>
|
||||
{value.markdown.title}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<li key={key}>
|
||||
<span>{key}</span>
|
||||
{renderTree(value, `${basePath}/${key}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="side-navigation">
|
||||
<h3>Markdown Directory</h3>
|
||||
{tree ? renderTree(tree) : <p>Loading...</p>}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideNavigation;
|
||||
24
src/utils/fetchWIthCache.js
Normal file
24
src/utils/fetchWIthCache.js
Normal file
@@ -0,0 +1,24 @@
|
||||
export async function fetchWithCache(url, cacheKey = url, cacheExpiry = 60) {
|
||||
const cachedData = localStorage.getItem(cacheKey);
|
||||
const now = Date.now();
|
||||
|
||||
if (cachedData) {
|
||||
const { data, timestamp } = JSON.parse(cachedData);
|
||||
if (now - timestamp < cacheExpiry * 1000) {
|
||||
console.log("Cache hit for:", url);
|
||||
return data;
|
||||
} else {
|
||||
console.log("Cache expired for:", url);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
localStorage.setItem(cacheKey, JSON.stringify({ data, timestamp: now }));
|
||||
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user