From 6d96b658f0b3477873bf8a2b4daecbd22e1c31c2 Mon Sep 17 00:00:00 2001 From: hzhang Date: Fri, 6 Dec 2024 10:04:03 +0000 Subject: [PATCH] kc token public key/token issue, path root set to 1 --- src/components/Markdowns/MarkdownEditor.js | 41 +++---- src/components/Navigations/MainNavigation.js | 12 +- src/components/Navigations/PathNode.js | 2 + src/components/PathManager.css | 57 +++++++++ src/components/PathManager.js | 116 +++++++++++++++++++ src/components/PermissionGuard.js | 1 - src/utils/requestUtils.js | 15 ++- 7 files changed, 215 insertions(+), 29 deletions(-) create mode 100644 src/components/PathManager.css create mode 100644 src/components/PathManager.js diff --git a/src/components/Markdowns/MarkdownEditor.js b/src/components/Markdowns/MarkdownEditor.js index 90eee44..999ead2 100644 --- a/src/components/Markdowns/MarkdownEditor.js +++ b/src/components/Markdowns/MarkdownEditor.js @@ -10,7 +10,8 @@ import { okaidia } from "react-syntax-highlighter/dist/esm/styles/prism"; import "katex/dist/katex.min.css"; import "./MarkdownEditor.css"; import config from "../../config"; -import {fetch_} from "../../utils/requestUtils"; +import { fetch_ } from "../../utils/requestUtils"; +import PathManager from "../PathManager"; const MarkdownEditor = () => { const { roles } = useContext(AuthContext); @@ -18,7 +19,7 @@ const MarkdownEditor = () => { const { id } = useParams(); const [title, setTitle] = useState(""); const [content, setContent] = useState(""); - const [path, setPath] = useState(""); + const [pathId, setPathId] = useState(1); useEffect(() => { if (id) { @@ -29,10 +30,10 @@ const MarkdownEditor = () => { .then((data) => { setTitle(data.title); setContent(data.content); - setPath(data.path); + setPathId(data.path_id); }) .catch((err) => { - console.error("failed to load markdown", err); + console.error("Failed to load markdown", err); }); } }, [id]); @@ -42,26 +43,27 @@ const MarkdownEditor = () => { const method = id ? "PUT" : "POST"; fetch_(url, { method, - body: JSON.stringify({ title, content, path }), + body: JSON.stringify({ title, content, path_id: pathId }), }, { use_cache: false, use_token: true, }).then((res) => { - if(res.ok) + if (res.ok) { navigate("/"); - else + } else { return res.json().then((data) => { - throw new Error(data.error || "Failed to load markdown"); + throw new Error(data.error || "Failed to save markdown"); }); + } }).catch((err) => { - console.error("failed to load markdown", err); - }) - + console.error("Failed to save markdown", err); + }); }; const hasPermission = roles.includes("admin") || roles.includes("creator"); - if (!hasPermission) + if (!hasPermission) { return
Permission Denied
; + } return (
@@ -84,18 +86,13 @@ const MarkdownEditor = () => {
- {/* Path Field */} + {/* PathManager Field */}
-
- setPath(e.target.value)} - /> -
+
{/* Content Field */} diff --git a/src/components/Navigations/MainNavigation.js b/src/components/Navigations/MainNavigation.js index 5be7605..265feae 100644 --- a/src/components/Navigations/MainNavigation.js +++ b/src/components/Navigations/MainNavigation.js @@ -58,14 +58,22 @@ const MainNavigation = () => { {user.profile.name} - ) : (
-
diff --git a/src/components/Navigations/PathNode.js b/src/components/Navigations/PathNode.js index d0651ac..37abad5 100644 --- a/src/components/Navigations/PathNode.js +++ b/src/components/Navigations/PathNode.js @@ -118,12 +118,14 @@ const PathNode = ({ path, isRoot = false }) => { diff --git a/src/components/PathManager.css b/src/components/PathManager.css new file mode 100644 index 0000000..2c6e82a --- /dev/null +++ b/src/components/PathManager.css @@ -0,0 +1,57 @@ +.path-manager { + border: 1px solid #ddd; + border-radius: 8px; + padding: 1rem; + background-color: #f9f9f9; +} + +.path-manager-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} + +.current-path { + font-weight: bold; + font-size: 1rem; +} + +.sub-paths { + list-style: none; + padding: 0; + margin: 0; +} + +.sub-path-item { + padding: 0.5rem; + font-size: 1rem; + color: #4a4a4a; + border-bottom: 1px solid #ddd; + cursor: pointer; + transition: background-color 0.2s; +} + +.sub-path-item:last-child { + border-bottom: none; +} + +.sub-path-item:hover { + background-color: #f0f0f0; + color: #00d1b2; +} + +.path-manager-footer { + margin-top: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.path-manager-footer .input { + flex: 1; +} + +.path-manager-footer .button { + white-space: nowrap; +} \ No newline at end of file diff --git a/src/components/PathManager.js b/src/components/PathManager.js new file mode 100644 index 0000000..9e58d4d --- /dev/null +++ b/src/components/PathManager.js @@ -0,0 +1,116 @@ +import React, { useEffect, useState } from "react"; +import { fetch_ } from "../utils/requestUtils"; +import config from "../config"; +import "./PathManager.css"; + +const PathManager = ({ currentPathId = 1, onPathChange }) => { + const [currentPath, setCurrentPath] = useState(""); + const [currentId, setCurrentId] = useState(currentPathId); + const [subPaths, setSubPaths] = useState([]); + const [newDirName, setNewDirName] = useState(""); + const [loading, setLoading] = useState(false); + + useEffect(() => { + fetchSubPaths(currentId); + }, [currentId]); + + const fetchSubPaths = (pathId) => { + setLoading(true); + fetch_(`${config.BACKEND_HOST}/api/path/parent/${pathId}`, {}, { use_cache: false, use_token: true }) + .then((data) => setSubPaths(data)) + .catch((error) => console.error("Failed to fetch subdirectories:", error)) + .finally(() => setLoading(false)); + }; + + const handleSubPathClick = (subPath) => { + setCurrentPath(`${currentPath}/${subPath.name}`); + setCurrentId(subPath.id); + onPathChange(subPath.id); + }; + + const handleBackClick = () => { + if (currentId === 1) return; + const pathSegments = currentPath.split("/").filter(Boolean); + pathSegments.pop(); + setCurrentPath(pathSegments.length > 0 ? `/${pathSegments.join("/")}` : ""); + const parentId = pathSegments.length > 0 + ? subPaths.find((path) => path.name === pathSegments[pathSegments.length - 1])?.id || 0 + : 1; + setCurrentId(parentId); + onPathChange(parentId); + }; + + const handleAddDirectory = () => { + if (!newDirName.trim()) { + alert("Directory name cannot be empty."); + return; + } + fetch_(`${config.BACKEND_HOST}/api/path/`, { + method: "POST", + body: JSON.stringify({ name: newDirName.trim(), parent_id: currentId }), + }, { use_cache: false, use_token: true }) + .then((newDir) => { + setSubPaths([...subPaths, newDir]); + setNewDirName(""); + alert("Directory created successfully!"); + }) + .catch((error) => { + console.error("Failed to create directory:", error); + alert("Failed to create directory."); + }); + }; + + return ( +
+
+ +
Current Path: {currentPath || "/"}
+
+ +
+ {loading ? ( +

Loading subdirectories...

+ ) : ( +
    + {subPaths.map((subPath) => ( +
  • handleSubPathClick(subPath)} + > + {subPath.name} +
  • + ))} +
+ )} +
+ +
+ setNewDirName(e.target.value)} + /> + +
+
+ ); +}; + +export default PathManager; \ No newline at end of file diff --git a/src/components/PermissionGuard.js b/src/components/PermissionGuard.js index c032e6f..11c313b 100644 --- a/src/components/PermissionGuard.js +++ b/src/components/PermissionGuard.js @@ -6,7 +6,6 @@ const PermissionGuard = ({rolesRequired, children}) => { const hasPermission = rolesRequired.some((role) => roles.includes(role)); if (!hasPermission) { - console.log("F"); return null; } return children; diff --git a/src/utils/requestUtils.js b/src/utils/requestUtils.js index fbf055f..998fbf5 100644 --- a/src/utils/requestUtils.js +++ b/src/utils/requestUtils.js @@ -1,3 +1,5 @@ +import {data} from "react-router-dom"; + const ongoingRequests = new Map(); @@ -40,9 +42,15 @@ export async function fetch_(url, init = {}, init_options = {}){ headers: { ...(init.headers || {}), ...(token ? {Authorization: `Bearer ${token}`} : {}), + ...(init.method && ['PUT', 'POST'].includes(init.method.toUpperCase()) + ? {'Content-Type': 'application/json'} + : {}), } }; + if(options.use_cache && ongoingRequests.has(options.cache_key)){ + return ongoingRequests.get(options.cache_key); + } const now = Date.now(); const cached_data = localStorage.getItem(options.cache_key); if(options.use_cache && cached_data){ @@ -54,11 +62,13 @@ export async function fetch_(url, init = {}, init_options = {}){ try { const fetchPromise = fetch(url, request_options) .then((response) => { - if(!response.ok) + if(!response.ok) { throw new Error(`RESPONSE_ERROR: ${response.status}`); + } return response.json(); }) .then((data) => { + if (options.use_cache) { localStorage.setItem( @@ -79,9 +89,6 @@ export async function fetch_(url, init = {}, init_options = {}){ } throw error; } - - - }