diff --git a/src/App.css b/src/App.css
new file mode 100644
index 0000000..18a6ab5
--- /dev/null
+++ b/src/App.css
@@ -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;
+}
\ No newline at end of file
diff --git a/src/components/MainNavigation.css b/src/components/MainNavigation.css
new file mode 100644
index 0000000..79a99e6
--- /dev/null
+++ b/src/components/MainNavigation.css
@@ -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;
+}
\ No newline at end of file
diff --git a/src/components/MainNavigation.js b/src/components/MainNavigation.js
new file mode 100644
index 0000000..2e41d65
--- /dev/null
+++ b/src/components/MainNavigation.js
@@ -0,0 +1,32 @@
+//src/components/MainNavigation.js
+
+import React from "react";
+import { Link } from "react-router-dom";
+import "./MainNavigation.css";
+
+const MainNavigation = () => {
+ return (
+
+ );
+};
+
+export default MainNavigation;
\ No newline at end of file
diff --git a/src/components/MarkdownContent.css b/src/components/MarkdownContent.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/MarkdownContent.js b/src/components/MarkdownContent.js
new file mode 100644
index 0000000..62a547b
--- /dev/null
+++ b/src/components/MarkdownContent.js
@@ -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
Error: {error}
;
+ }
+
+ if (!content) {
+ return Loading...
;
+ }
+
+ return (
+
+ );
+};
+
+export default MarkdownContent;
\ No newline at end of file
diff --git a/src/components/SideNavigation.css b/src/components/SideNavigation.css
new file mode 100644
index 0000000..00d8d8c
--- /dev/null
+++ b/src/components/SideNavigation.css
@@ -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;
+}
\ No newline at end of file
diff --git a/src/components/SideNavigation.js b/src/components/SideNavigation.js
new file mode 100644
index 0000000..3757a3d
--- /dev/null
+++ b/src/components/SideNavigation.js
@@ -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 (
+
+ {Object.entries(node).map(([key, value]) => {
+ if (value.markdown) {
+ return (
+ -
+
+ {value.markdown.title}
+
+
+ );
+ }
+ return (
+ -
+ {key}
+ {renderTree(value, `${basePath}/${key}`)}
+
+ );
+ })}
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+export default SideNavigation;
\ No newline at end of file
diff --git a/src/utils/fetchWIthCache.js b/src/utils/fetchWIthCache.js
new file mode 100644
index 0000000..808845e
--- /dev/null
+++ b/src/utils/fetchWIthCache.js
@@ -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;
+}
\ No newline at end of file