206 lines
8.6 KiB
JavaScript
206 lines
8.6 KiB
JavaScript
import React, {useState} from "react";
|
|
import { Link } from "react-router-dom";
|
|
import PermissionGuard from "../PermissionGuard";
|
|
import "./PathNode.css";
|
|
import {useDeletePath, useMovePath, usePaths, useUpdatePath} from "../../utils/path-queries";
|
|
import {useIndexMarkdown, useMarkdownsByPath, useMoveMarkdown} from "../../utils/markdown-queries";
|
|
|
|
const PathNode = ({ path, isRoot = false }) => {
|
|
const [isExpanded, setIsExpanded] = useState(isRoot);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [newName, setNewName] = useState(path.name);
|
|
|
|
const { data: childPaths, isLoading: isChildLoading, error: childError } = usePaths(path.id);
|
|
const { data: markdowns, isLoading: isMarkdownLoading, error: markdownError } = useMarkdownsByPath(path.id);
|
|
const deletePath = useDeletePath();
|
|
const updatePath = useUpdatePath();
|
|
|
|
const {data: indexMarkdown, isLoading: isIndexLoading, error: indexMarkdownError} = useIndexMarkdown(path.id);
|
|
|
|
const movePath = useMovePath();
|
|
const moveMarkdown = useMoveMarkdown();
|
|
|
|
const toggleExpand = () => {
|
|
setIsExpanded(!isExpanded);
|
|
};
|
|
|
|
const handleSave = () => {
|
|
console.log(`handleSave ${path.id}`);
|
|
updatePath.mutate({id: path.id, data: {name: newName}}, {
|
|
onsuccess: () => setIsEditing(false),
|
|
onError: err => alert("failed to update this path"),
|
|
})
|
|
};
|
|
const handleDelete = () => {
|
|
if(window.confirm("Are you sure?")) {
|
|
deletePath.mutate(path.id, {
|
|
onError: err => alert("failed to delete this path"),
|
|
})
|
|
}
|
|
};
|
|
const handleEdit = () => {
|
|
setIsEditing(true);
|
|
};
|
|
|
|
const handleMovePath = (pth, direction) => {
|
|
movePath.mutate({path: pth, direction: direction}, {
|
|
onError: () => alert("failed to move this path"),
|
|
});
|
|
};
|
|
|
|
const handleMoveMarkdown = (md, direction) => {
|
|
moveMarkdown.mutate({markdown: md, direction: direction}, {
|
|
onError: () => alert("failed to move this markdown"),
|
|
})
|
|
};
|
|
|
|
const sortedPaths = childPaths
|
|
? childPaths.slice().sort((a, b) => a.order.localeCompare(b.order))
|
|
: [];
|
|
|
|
const sortedMarkdowns = markdowns
|
|
? markdowns.filter(md => md.title !== "index").sort((a, b) => a.order.localeCompare(b.order))
|
|
: [];
|
|
|
|
if(childError || markdownError){
|
|
return <li>Error...</li>;
|
|
}
|
|
|
|
return (
|
|
<li>
|
|
<div className="path-node-header field has-addons" >
|
|
{isEditing ? (
|
|
<div className = "control has-icons-left">
|
|
<input
|
|
className="input is-small path-edit-input"
|
|
value={newName}
|
|
onChange={(e) => setNewName(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
) : (
|
|
<span
|
|
className="path-name has-text-weight-bold control"
|
|
onClick={isRoot ? undefined : toggleExpand}
|
|
>
|
|
{
|
|
indexMarkdown ? (
|
|
<Link to={`/markdown/${indexMarkdown.id}`} className="is-link">
|
|
{path.name}
|
|
</Link>
|
|
) : (
|
|
<a className="is-link">{path.name}</a>
|
|
)
|
|
}
|
|
|
|
</span>
|
|
)}
|
|
|
|
<PermissionGuard rolesRequired={["admin"]}>
|
|
<div className="field has-addons actions control is-justify-content-flex-end">
|
|
{isEditing ? (
|
|
<p className="control">
|
|
<button
|
|
className="button is-small is-success"
|
|
onClick={handleSave}
|
|
type="button"
|
|
>
|
|
<span className="icon">
|
|
<i className="fas fa-check"></i>
|
|
</span>
|
|
</button>
|
|
</p>
|
|
) : (
|
|
<p className="control">
|
|
<button
|
|
className="button is-small is-info"
|
|
onClick={handleEdit}
|
|
type="button"
|
|
>
|
|
<span className="icon">
|
|
<i className="fas fa-pen"></i>
|
|
</span>
|
|
</button>
|
|
</p>
|
|
)}
|
|
<p className="control">
|
|
<button
|
|
className="button is-danger is-small"
|
|
onClick={handleDelete}
|
|
type="button">
|
|
<span className="icon">
|
|
<i className="fas fa-trash"></i>
|
|
</span>
|
|
</button>
|
|
</p>
|
|
<div
|
|
className="control is-flex is-flex-direction-column is-align-items-center"
|
|
style={{marginLeft: "0.5rem"}}
|
|
>
|
|
<button
|
|
className="button is-small is-primary mb-1"
|
|
style={{height: "1rem", padding: "0.25rem"}}
|
|
onClick={() => handleMovePath(path, "forward")}
|
|
type="button"
|
|
>
|
|
</button>
|
|
<button
|
|
className="button is-small is-primary mb-1"
|
|
style={{height: "1rem", padding: "0.25rem"}}
|
|
onClick={() => handleMovePath(path, "backward")}
|
|
type="button"
|
|
>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</PermissionGuard>
|
|
</div>
|
|
|
|
{isExpanded && (
|
|
<ul>
|
|
{isChildLoading && <p>Loading...</p>}
|
|
{sortedPaths.map((child) => (
|
|
<PathNode
|
|
key={child.id}
|
|
path={child}
|
|
/>
|
|
))}
|
|
|
|
{sortedMarkdowns.filter(md => md.title !== "index").map((markdown) => (
|
|
<li key={markdown.id}>
|
|
<div className="is-clickable field has-addons">
|
|
<span className="markdown-name has-text-weight-bold control">
|
|
<Link to={`/markdown/${markdown.id}`} className="is-link">
|
|
{markdown.title}
|
|
</Link>
|
|
</span>
|
|
<PermissionGuard rolesRequired={['admin']}>
|
|
<div className="control">
|
|
<button
|
|
className="button is-small is-primary mb-1"
|
|
style={{ height: "1rem", padding: "0.25rem" }}
|
|
onClick={() => handleMoveMarkdown(markdown, "forward")}
|
|
type="button"
|
|
>
|
|
</button>
|
|
<button
|
|
className="button is-small is-primary mb-1"
|
|
style={{ height: "1rem", padding: "0.25rem" }}
|
|
onClick={() => handleMoveMarkdown(markdown, "backward")}
|
|
type="button"
|
|
>
|
|
</button>
|
|
</div>
|
|
</PermissionGuard>
|
|
</div>
|
|
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</li>
|
|
);
|
|
};
|
|
|
|
export default PathNode;
|