Compare commits
5 Commits
87b4246a9b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c9310250e4 | |||
| a08164e914 | |||
| 30a46d5064 | |||
| e5affe3465 | |||
| 101666d26d |
@@ -5,7 +5,7 @@ FRONTEND_HOST="${FRONTEND_HOST:-http://localhost:80}"
|
|||||||
KC_CLIENT_ID="${KC_CLIENT_ID:-labdev}"
|
KC_CLIENT_ID="${KC_CLIENT_ID:-labdev}"
|
||||||
KC_HOST="${KC_HOST:-https://login.hangman-lab.top}"
|
KC_HOST="${KC_HOST:-https://login.hangman-lab.top}"
|
||||||
KC_REALM="${KC_REALM:-Hangman-Lab}"
|
KC_REALM="${KC_REALM:-Hangman-Lab}"
|
||||||
|
DEBUG="${DEBUG:false}"
|
||||||
rm -f /usr/share/nginx/html/config.js
|
rm -f /usr/share/nginx/html/config.js
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,8 @@ cat <<EOL > /usr/share/nginx/html/config.json
|
|||||||
"scope": "openid profile email roles",
|
"scope": "openid profile email roles",
|
||||||
"popup_redirect_uri": "${FRONTEND_HOST}/popup_callback",
|
"popup_redirect_uri": "${FRONTEND_HOST}/popup_callback",
|
||||||
"silent_redirect_uri": "${FRONTEND_HOST}/silent_callback"
|
"silent_redirect_uri": "${FRONTEND_HOST}/silent_callback"
|
||||||
}
|
},
|
||||||
|
"DEBUG": ${DEBUG}
|
||||||
}
|
}
|
||||||
EOL
|
EOL
|
||||||
|
|
||||||
|
|||||||
57
package-lock.json
generated
57
package-lock.json
generated
@@ -10,7 +10,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^2.7.0",
|
"@reduxjs/toolkit": "^2.7.0",
|
||||||
"@tanstack/react-query": "^4.36.1",
|
"@tanstack/react-query": "^5.75.5",
|
||||||
|
"@tanstack/react-query-devtools": "^5.75.5",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bulma": "^1.0.2",
|
"bulma": "^1.0.2",
|
||||||
@@ -2712,38 +2713,56 @@
|
|||||||
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
|
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "4.36.1",
|
"version": "5.75.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.75.5.tgz",
|
||||||
"integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
|
"integrity": "sha512-kPDOxtoMn2Ycycb76Givx2fi+2pzo98F9ifHL/NFiahEDpDwSVW6o12PRuQ0lQnBOunhRG5etatAhQij91M3MQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/query-devtools": {
|
||||||
|
"version": "5.74.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.74.7.tgz",
|
||||||
|
"integrity": "sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw==",
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-query": {
|
"node_modules/@tanstack/react-query": {
|
||||||
"version": "4.36.1",
|
"version": "5.75.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.75.5.tgz",
|
||||||
"integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
|
"integrity": "sha512-QrLCJe40BgBVlWdAdf2ZEVJ0cISOuEy/HKupId1aTKU6gPJZVhSvZpH+Si7csRflCJphzlQ77Yx6gUxGW9o0XQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/query-core": "4.36.1",
|
"@tanstack/query-core": "5.75.5"
|
||||||
"use-sync-external-store": "^1.2.0"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
"react": "^18 || ^19"
|
||||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
}
|
||||||
"react-native": "*"
|
},
|
||||||
|
"node_modules/@tanstack/react-query-devtools": {
|
||||||
|
"version": "5.75.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.75.5.tgz",
|
||||||
|
"integrity": "sha512-S31U00nJOQIbxydRH1kOwdLRaLBrda8O5QjzmgkRg60UZzPGdbI6+873Qa0YGUfPeILDbR2ukgWyg7CJQPy4iA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-devtools": "5.74.7"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"funding": {
|
||||||
"react-dom": {
|
"type": "github",
|
||||||
"optional": true
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
},
|
},
|
||||||
"react-native": {
|
"peerDependencies": {
|
||||||
"optional": true
|
"@tanstack/react-query": "^5.75.5",
|
||||||
}
|
"react": "^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^2.7.0",
|
"@reduxjs/toolkit": "^2.7.0",
|
||||||
"@tanstack/react-query": "^4.36.1",
|
"@tanstack/react-query": "^5.75.5",
|
||||||
|
"@tanstack/react-query-devtools": "^5.75.5",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bulma": "^1.0.2",
|
"bulma": "^1.0.2",
|
||||||
|
|||||||
54
src/App.js
54
src/App.js
@@ -6,6 +6,7 @@ import MainNavigation from "./components/Navigations/MainNavigation";
|
|||||||
import SideNavigation from "./components/Navigations/SideNavigation";
|
import SideNavigation from "./components/Navigations/SideNavigation";
|
||||||
import MarkdownContent from "./components/Markdowns/MarkdownContent";
|
import MarkdownContent from "./components/Markdowns/MarkdownContent";
|
||||||
import MarkdownEditor from "./components/Markdowns/MarkdownEditor";
|
import MarkdownEditor from "./components/Markdowns/MarkdownEditor";
|
||||||
|
import StandaloneMarkdownPage from "./components/Markdowns/StandaloneMarkdownPage";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import Callback from "./components/KeycloakCallbacks/Callback";
|
import Callback from "./components/KeycloakCallbacks/Callback";
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
@@ -18,30 +19,35 @@ const App = () => {
|
|||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router>
|
<Router>
|
||||||
<div className="app-container">
|
<Routes>
|
||||||
<MainNavigation />
|
<Route path="/pg/*" element={<StandaloneMarkdownPage />} />
|
||||||
<div className="content-container">
|
<Route path="*" element={
|
||||||
<SideNavigation />
|
<div className="app-container">
|
||||||
<main className="main-content">
|
<MainNavigation />
|
||||||
<Routes>
|
<div className="content-container">
|
||||||
<Route
|
<SideNavigation />
|
||||||
path="/"
|
<main className="main-content">
|
||||||
element={<Navigate to = "/markdown/1"/>}
|
<Routes>
|
||||||
/>
|
<Route
|
||||||
<Route path="/testx" element={<h2>test2</h2>}/>
|
path="/"
|
||||||
<Route path="/markdown/:id" element={<MarkdownContent />} />
|
element={<Navigate to = "/markdown/1"/>}
|
||||||
<Route path="/callback" element={<Callback />} />
|
/>
|
||||||
<Route path="/test" element={<h1>TEST</h1>}></Route>
|
<Route path="/testx" element={<h2>test2</h2>}/>
|
||||||
<Route path="/markdown/create" element={<MarkdownEditor />} />
|
<Route path="/markdown/:strId" element={<MarkdownContent />} />
|
||||||
<Route path="/markdown/edit/:id" element={<MarkdownEditor />} />
|
<Route path="/callback" element={<Callback />} />
|
||||||
<Route path="/popup_callback" element={<PopupCallback />} />
|
<Route path="/test" element={<h1>TEST</h1>}></Route>
|
||||||
<Route path="/silent_callback" element={<SilentCallback />} />
|
<Route path="/markdown/create" element={<MarkdownEditor />} />
|
||||||
<Route path="/template/create" element={<MarkdownTemplateEditor />} />
|
<Route path="/markdown/edit/:strId" element={<MarkdownEditor />} />
|
||||||
<Route path="/template/edit/:id" element={<MarkdownTemplateEditor />} />
|
<Route path="/popup_callback" element={<PopupCallback />} />
|
||||||
</Routes>
|
<Route path="/silent_callback" element={<SilentCallback />} />
|
||||||
</main>
|
<Route path="/template/create" element={<MarkdownTemplateEditor />} />
|
||||||
</div>
|
<Route path="/template/edit/:strId" element={<MarkdownTemplateEditor />} />
|
||||||
</div>
|
</Routes>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} />
|
||||||
|
</Routes>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Router>
|
</Router>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
14
src/components/Debug/ControlledReactQueryDevtools.js
Normal file
14
src/components/Debug/ControlledReactQueryDevtools.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {useConfig} from "../../ConfigProvider";
|
||||||
|
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
|
||||||
|
|
||||||
|
export const ControlledReactQueryDevtools = () => {
|
||||||
|
const config = useConfig();
|
||||||
|
if(config.DEBUG)
|
||||||
|
return (<ReactQueryDevtools />);
|
||||||
|
|
||||||
|
return (<></>);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ControlledReactQueryDevtools;
|
||||||
@@ -5,6 +5,7 @@ const LayoutEditor = ({layout, onChange}) => {
|
|||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
className="textarea"
|
className="textarea"
|
||||||
|
style={{ height: "60vh" }}
|
||||||
value={_layout}
|
value={_layout}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setLayout(e.target.value);
|
setLayout(e.target.value);
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ const MarkdownTemplateEditor = () => {
|
|||||||
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id } = useParams();
|
const { strId } = useParams();
|
||||||
|
const id = Number(strId);
|
||||||
const { data: template, isFetching: templateIsFetching } = useMarkdownTemplate(id);
|
const { data: template, isFetching: templateIsFetching } = useMarkdownTemplate(id);
|
||||||
const saveMarkdownTemplate = useSaveMarkdownTemplate();
|
const saveMarkdownTemplate = useSaveMarkdownTemplate();
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ const MarkdownTemplateEditor = () => {
|
|||||||
<div className="columns is-variable is-8">
|
<div className="columns is-variable is-8">
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<h3 className="title is-5">Layout</h3>
|
<h3 className="title is-5">Layout</h3>
|
||||||
|
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<LayoutEditor
|
<LayoutEditor
|
||||||
layout={layout}
|
layout={layout}
|
||||||
@@ -71,15 +73,15 @@ const MarkdownTemplateEditor = () => {
|
|||||||
onChange={(newLayout) => setLayout(newLayout)}
|
onChange={(newLayout) => setLayout(newLayout)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<h3 className="title is-5">Parameters</h3>
|
<h3 className="title is-5">Parameters</h3>
|
||||||
<div className="box">
|
<ParametersManager
|
||||||
<ParametersManager
|
parameters={parameters}
|
||||||
parameters={parameters}
|
onChange={(newParameters) => setParameters(newParameters)}
|
||||||
onChange={(newParameters) => setParameters(newParameters)}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field is-grouped">
|
<div className="field is-grouped">
|
||||||
|
|||||||
@@ -3,18 +3,36 @@ import TypeEditor from "./TypeEditor";
|
|||||||
|
|
||||||
const ParametersManager = ({ parameters, onChange }) => {
|
const ParametersManager = ({ parameters, onChange }) => {
|
||||||
const [_parameters, setParameters] = useState(parameters || []);
|
const [_parameters, setParameters] = useState(parameters || []);
|
||||||
|
const [expandedStates, setExpandedStates] = useState({});
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
const updated = [
|
const updated = [
|
||||||
..._parameters,
|
..._parameters,
|
||||||
{
|
{
|
||||||
name: "",
|
name: "",
|
||||||
type: { base_type: "string" }
|
type: {
|
||||||
|
base_type: "string",
|
||||||
|
definition: {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
setParameters(updated);
|
setParameters(updated);
|
||||||
onChange(updated);
|
onChange(updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTypeChange = (index, newType) => {
|
||||||
|
const updated = [..._parameters];
|
||||||
|
if (newType.base_type === "list" && !newType.extend_type) {
|
||||||
|
newType.extend_type = {
|
||||||
|
base_type: "string",
|
||||||
|
definition: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
updated[index].type = newType;
|
||||||
|
setParameters(updated);
|
||||||
|
onChange(updated);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setParameters(parameters);
|
setParameters(parameters);
|
||||||
}, [parameters]);
|
}, [parameters]);
|
||||||
@@ -33,6 +51,13 @@ const ParametersManager = ({ parameters, onChange }) => {
|
|||||||
onChange(updated);
|
onChange(updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleExpand = (index) => {
|
||||||
|
setExpandedStates(prev => ({
|
||||||
|
...prev,
|
||||||
|
[index]: !prev[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="field">
|
<div className="field">
|
||||||
@@ -42,44 +67,53 @@ const ParametersManager = ({ parameters, onChange }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{_parameters.map((param, index) => (
|
<div style={{ maxHeight: "50vh", overflowY: "auto" }}>
|
||||||
<div key={index} className="box" style={{ marginBottom: "1rem" }}>
|
{_parameters.map((param, index) => (
|
||||||
<div className="field is-grouped is-grouped-multiline">
|
<div key={index} className="box" style={{ marginBottom: "0.5rem" }}>
|
||||||
<div className="control is-expanded">
|
<div className="field is-grouped is-align-items-end">
|
||||||
<label className="label">Name:</label>
|
<div className="control">
|
||||||
<input
|
<label className="label">Name:</label>
|
||||||
type="text"
|
</div>
|
||||||
className="input"
|
<div className="control is-expanded">
|
||||||
value={param.name}
|
<input
|
||||||
onChange={(e) => handleNameChange(index, e.target.value)}
|
type="text"
|
||||||
placeholder="Parameter name"
|
className="input"
|
||||||
/>
|
value={param.name}
|
||||||
|
onChange={(e) => handleNameChange(index, e.target.value)}
|
||||||
|
placeholder="Parameter name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="control">
|
||||||
|
<button
|
||||||
|
className="button is-danger"
|
||||||
|
onClick={() => handleDelete(index)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="control">
|
<div className="field">
|
||||||
<button
|
<div className="is-flex is-justify-content-space-between is-align-items-center mb-1">
|
||||||
className="button is-danger"
|
<label className="label mb-0">Type:</label>
|
||||||
onClick={() => handleDelete(index)}
|
<button
|
||||||
>
|
className="button is-small"
|
||||||
Delete
|
onClick={() => toggleExpand(index)}
|
||||||
</button>
|
>
|
||||||
|
{expandedStates[index] ? "-" : "+"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{expandedStates[index] && (
|
||||||
|
<div className="control">
|
||||||
|
<TypeEditor
|
||||||
|
type={param.type}
|
||||||
|
onChange={(newType) => handleTypeChange(index, newType)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
))}
|
||||||
<label className="label">Type:</label>
|
</div>
|
||||||
<div className="control">
|
|
||||||
<TypeEditor
|
|
||||||
type={param.type}
|
|
||||||
onChange={(newType) => {
|
|
||||||
const updated = [..._parameters];
|
|
||||||
updated[index].type = newType;
|
|
||||||
setParameters(updated);
|
|
||||||
onChange(updated);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useMarkdownTemplates } from "../../utils/queries/markdown-template-queries";
|
import { useMarkdownTemplates } from "../../utils/queries/markdown-template-queries";
|
||||||
|
|
||||||
const TemplateSelector = ({ template, onChange }) => {
|
const TemplateSelector = ({ template, onChange, onCreate }) => {
|
||||||
const { data: templates, isFetching: templatesAreFetching } = useMarkdownTemplates();
|
const { data: templates, isFetching: templatesAreFetching } = useMarkdownTemplates();
|
||||||
const [_template, setTemplate] = useState(
|
const [_template, setTemplate] = useState(
|
||||||
templates?.find((t) => t.id === template?.id) || {
|
templates?.find((t) => t.id === template?.id) || {
|
||||||
@@ -34,13 +34,15 @@ const TemplateSelector = ({ template, onChange }) => {
|
|||||||
value={template?.id || ""}
|
value={template?.id || ""}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const id = parseInt(e.target.value, 10);
|
const id = parseInt(e.target.value, 10);
|
||||||
onChange(
|
const selectedTemplate = templates.find((t) => t.id === id) || {
|
||||||
templates.find((t) => t.id === id) || {
|
title: "",
|
||||||
title: "",
|
parameters: [],
|
||||||
parameters: [],
|
layout: "",
|
||||||
layout: "",
|
};
|
||||||
}
|
onChange(selectedTemplate);
|
||||||
);
|
if (onCreate) {
|
||||||
|
onCreate(selectedTemplate);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">(None)</option>
|
<option value="">(None)</option>
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ const TypeEditor = ({ type, onChange }) => {
|
|||||||
case 'template':
|
case 'template':
|
||||||
return (
|
return (
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Template</label>
|
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<TemplateSelector
|
<TemplateSelector
|
||||||
template={_type.definition.template}
|
template={_type.definition.template}
|
||||||
@@ -95,7 +94,7 @@ const TypeEditor = ({ type, onChange }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Type</label>
|
{/*<label className="label">Type</label>*/}
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<div className="select is-fullwidth">
|
<div className="select is-fullwidth">
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -9,10 +9,13 @@ import {usePath} from "../../utils/queries/path-queries";
|
|||||||
import {useMarkdownSetting} from "../../utils/queries/markdown-setting-queries";
|
import {useMarkdownSetting} from "../../utils/queries/markdown-setting-queries";
|
||||||
import {useMarkdownTemplate} from "../../utils/queries/markdown-template-queries";
|
import {useMarkdownTemplate} from "../../utils/queries/markdown-template-queries";
|
||||||
import {useMarkdownTemplateSetting} from "../../utils/queries/markdown-template-setting-queries";
|
import {useMarkdownTemplateSetting} from "../../utils/queries/markdown-template-setting-queries";
|
||||||
|
import MarkdownSettingModal from "../Modals/MarkdownSettingModal";
|
||||||
|
|
||||||
const MarkdownContent = () => {
|
const MarkdownContent = () => {
|
||||||
const { id } = useParams();
|
const { strId } = useParams();
|
||||||
|
const id = Number(strId);
|
||||||
const [indexTitle, setIndexTitle] = useState(null);
|
const [indexTitle, setIndexTitle] = useState(null);
|
||||||
|
const [isSettingModalOpen, setSettingModalOpen] = useState(false);
|
||||||
const {data: markdown, isLoading, error} = useMarkdown(id);
|
const {data: markdown, isLoading, error} = useMarkdown(id);
|
||||||
const {data: path, isFetching: isPathFetching} = usePath(markdown?.path_id);
|
const {data: path, isFetching: isPathFetching} = usePath(markdown?.path_id);
|
||||||
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.setting_id);
|
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.setting_id);
|
||||||
@@ -35,8 +38,8 @@ const MarkdownContent = () => {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return <div>Error: {error.message || "Failed to load content"}</div>;
|
return <div>Error: {error.message || "Failed to load content"}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markdown.isMessage) {
|
if (markdown.isMessage) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="markdown-content-container">
|
<div className="markdown-content-container">
|
||||||
<div className="notification is-info">
|
<div className="notification is-info">
|
||||||
@@ -49,15 +52,28 @@ const MarkdownContent = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="markdown-content-container">
|
<div className="markdown-content-container">
|
||||||
<div className="field has-addons markdown-content-container-header">
|
<div className="is-flex is-justify-content-space-between is-align-items-center markdown-content-container-header">
|
||||||
<h1 className="title control">{markdown.title === "index" ? indexTitle : markdown.title}</h1>
|
<h1 className="title">{markdown.title === "index" ? indexTitle : markdown.title}</h1>
|
||||||
<PermissionGuard rolesRequired={['admin']}>
|
<PermissionGuard rolesRequired={['admin']}>
|
||||||
<Link to={`/markdown/edit/${id}`} className="control button is-primary is-light">
|
<div className="field has-addons">
|
||||||
Edit
|
<button
|
||||||
</Link>
|
className="control button is-info is-light"
|
||||||
|
onClick={() => setSettingModalOpen(true)}
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</button>
|
||||||
|
<Link to={`/markdown/edit/${id}`} className="control button is-primary is-light">
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</PermissionGuard>
|
</PermissionGuard>
|
||||||
</div>
|
</div>
|
||||||
<MarkdownView content={JSON.parse(markdown.content)} template={template}/>
|
<MarkdownView content={JSON.parse(markdown.content)} template={template}/>
|
||||||
|
<MarkdownSettingModal
|
||||||
|
isOpen={isSettingModalOpen}
|
||||||
|
markdown={markdown}
|
||||||
|
onClose={() => setSettingModalOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import {useMarkdownTemplate, useMarkdownTemplates} from "../../utils/queries/mar
|
|||||||
import TemplatedEditor from "./TemplatedEditor";
|
import TemplatedEditor from "./TemplatedEditor";
|
||||||
import {useMarkdownTemplateSetting, useUpdateMarkdownTemplateSetting, useCreateMarkdownTemplateSetting} from "../../utils/queries/markdown-template-setting-queries";
|
import {useMarkdownTemplateSetting, useUpdateMarkdownTemplateSetting, useCreateMarkdownTemplateSetting} from "../../utils/queries/markdown-template-setting-queries";
|
||||||
import TemplateSelector from "../MarkdownTemplate/TemplateSelector";
|
import TemplateSelector from "../MarkdownTemplate/TemplateSelector";
|
||||||
|
import {useCreateMarkdownSetting} from "../../utils/queries/markdown-setting-queries";
|
||||||
|
|
||||||
const MarkdownEditor = () => {
|
const MarkdownEditor = () => {
|
||||||
const { roles } = useContext(AuthContext);
|
const { roles } = useContext(AuthContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id } = useParams();
|
const { strId } = useParams();
|
||||||
|
const id = Number(strId);
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [content, setContent] = useState({});
|
const [content, setContent] = useState({});
|
||||||
const [shortcut, setShortcut] = useState("");
|
const [shortcut, setShortcut] = useState("");
|
||||||
@@ -23,6 +25,7 @@ const MarkdownEditor = () => {
|
|||||||
const [isRawMode, setIsRawMode] = useState(false);
|
const [isRawMode, setIsRawMode] = useState(false);
|
||||||
const [rawContent, setRawContent] = useState("");
|
const [rawContent, setRawContent] = useState("");
|
||||||
const [jsonError, setJsonError] = useState("");
|
const [jsonError, setJsonError] = useState("");
|
||||||
|
const [selectedTemplate, setSelectedTemplate] = useState(null);
|
||||||
const {data: markdown, isFetching: isMarkdownFetching, error} = useMarkdown(id);
|
const {data: markdown, isFetching: isMarkdownFetching, error} = useMarkdown(id);
|
||||||
const saveMarkdown = useSaveMarkdown();
|
const saveMarkdown = useSaveMarkdown();
|
||||||
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.setting_id);
|
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.setting_id);
|
||||||
@@ -32,10 +35,12 @@ const MarkdownEditor = () => {
|
|||||||
const createTemplateSetting = useCreateMarkdownTemplateSetting();
|
const createTemplateSetting = useCreateMarkdownTemplateSetting();
|
||||||
const updateSetting = useUpdateMarkdownSetting();
|
const updateSetting = useUpdateMarkdownSetting();
|
||||||
const {data: templates, isFetching: templatesAreFetching} = useMarkdownTemplates();
|
const {data: templates, isFetching: templatesAreFetching} = useMarkdownTemplates();
|
||||||
|
const createMarkdownSetting = useCreateMarkdownSetting();
|
||||||
|
|
||||||
const notReady = isMarkdownFetching || isTemplateFetching || isSettingFetching || isTemplateSettingFetching || templatesAreFetching;
|
const notReady = isMarkdownFetching || isTemplateFetching || isSettingFetching || isTemplateSettingFetching || templatesAreFetching;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(markdown){
|
if (markdown) {
|
||||||
setTitle(markdown.title);
|
setTitle(markdown.title);
|
||||||
if (markdown.isMessage) {
|
if (markdown.isMessage) {
|
||||||
navigate("/");
|
navigate("/");
|
||||||
@@ -56,22 +61,84 @@ const MarkdownEditor = () => {
|
|||||||
}
|
}
|
||||||
}, [markdown, navigate]);
|
}, [markdown, navigate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (template) {
|
||||||
|
setSelectedTemplate(template);
|
||||||
|
}
|
||||||
|
}, [template]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (isRawMode && jsonError) {
|
if (isRawMode && jsonError) {
|
||||||
alert("Please fix the JSON errors before saving");
|
alert("Please fix the JSON errors before saving");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveMarkdown.mutate(
|
const saveData = {
|
||||||
{id, data: {title, content: JSON.stringify(content), path_id: pathId, shortcut}},
|
title,
|
||||||
{
|
content: JSON.stringify(content),
|
||||||
onSuccess: () => {
|
path_id: pathId,
|
||||||
navigate("/");
|
shortcut
|
||||||
},
|
};
|
||||||
onError: () => {
|
console.log("markdown", markdown);
|
||||||
alert("Error saving markdown file");
|
console.log(markdown?.id ? "update" : "create",)
|
||||||
|
if (!markdown?.id) {
|
||||||
|
saveMarkdown.mutate(
|
||||||
|
{data: saveData},
|
||||||
|
{
|
||||||
|
onSuccess: (newMarkdown) => {
|
||||||
|
createMarkdownSetting.mutate({}, {
|
||||||
|
onSuccess: (settingRes) => {
|
||||||
|
saveMarkdown.mutate({
|
||||||
|
id: newMarkdown.id,
|
||||||
|
data: {
|
||||||
|
setting_id: settingRes.id
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
onSuccess: () => {
|
||||||
|
if (selectedTemplate?.id) {
|
||||||
|
createTemplateSetting.mutate({
|
||||||
|
template_id: selectedTemplate.id
|
||||||
|
}, {
|
||||||
|
onSuccess: (templateSettingRes) => {
|
||||||
|
updateSetting.mutate({
|
||||||
|
id: settingRes.id,
|
||||||
|
data: {
|
||||||
|
template_setting_id: templateSettingRes.id
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
onSuccess: () => {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
alert("Error saving markdown file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
} else {
|
||||||
|
console.log("try update");
|
||||||
|
saveMarkdown.mutate(
|
||||||
|
{id, data: saveData},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
navigate("/markdown/" + id);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
alert("Error saving markdown file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleEditMode = () => {
|
const toggleEditMode = () => {
|
||||||
@@ -104,54 +171,17 @@ const MarkdownEditor = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTemplateChange = (newTemplate) => {
|
const handleTemplateChange = (newTemplate) => {
|
||||||
if (!newTemplate) return;
|
setSelectedTemplate(newTemplate);
|
||||||
|
|
||||||
if (templateSetting) {
|
if (templateSetting) {
|
||||||
updateTemplateSetting.mutate(
|
updateTemplateSetting.mutate({
|
||||||
{
|
id: templateSetting.id,
|
||||||
id: templateSetting.id,
|
data: {
|
||||||
data: { template_id: newTemplate.id }
|
template_id: newTemplate.id
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
setContent({});
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
alert("Error updating template");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
} else if (setting) {
|
|
||||||
createTemplateSetting.mutate(
|
|
||||||
{ template_id: newTemplate.id },
|
|
||||||
{
|
|
||||||
onSuccess: (newTemplateSetting) => {
|
|
||||||
updateSetting.mutate(
|
|
||||||
{
|
|
||||||
id: setting.id,
|
|
||||||
data: { template_setting_id: newTemplateSetting.id }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
setContent({});
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
alert("Error updating markdown setting");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
alert("Error creating template setting");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
alert("Cannot change template: No markdown setting found");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const hasPermission = roles.includes("admin") || roles.includes("creator");
|
const hasPermission = roles.includes("admin") || roles.includes("creator");
|
||||||
if (!hasPermission)
|
if (!hasPermission)
|
||||||
return <div className="notification is-danger">Permission Denied</div>;
|
return <div className="notification is-danger">Permission Denied</div>;
|
||||||
@@ -211,7 +241,7 @@ const MarkdownEditor = () => {
|
|||||||
<div className="field">
|
<div className="field">
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<TemplateSelector
|
<TemplateSelector
|
||||||
template={template}
|
template={selectedTemplate || template}
|
||||||
onChange={handleTemplateChange}
|
onChange={handleTemplateChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -247,9 +277,9 @@ const MarkdownEditor = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<TemplatedEditor
|
<TemplatedEditor
|
||||||
style={{height: "70vh"}}
|
style={{height: "40vh"}}
|
||||||
content={content}
|
content={content}
|
||||||
template={template}
|
template={!markdown?.id ? selectedTemplate : template}
|
||||||
onContentChanged={(k, v) => setContent(
|
onContentChanged={(k, v) => setContent(
|
||||||
prev => ({...prev, [k]: v})
|
prev => ({...prev, [k]: v})
|
||||||
)}
|
)}
|
||||||
@@ -275,7 +305,11 @@ const MarkdownEditor = () => {
|
|||||||
|
|
||||||
<div className="column is-half">
|
<div className="column is-half">
|
||||||
<h3 className="subtitle is-5">Preview</h3>
|
<h3 className="subtitle is-5">Preview</h3>
|
||||||
<MarkdownView content={content} template={template} height='70vh'/>
|
<MarkdownView
|
||||||
|
content={content}
|
||||||
|
template={!markdown?.id ? selectedTemplate : template}
|
||||||
|
height='70vh'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
100
src/components/Markdowns/StandaloneMarkdownPage.js
Normal file
100
src/components/Markdowns/StandaloneMarkdownPage.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import "katex/dist/katex.min.css";
|
||||||
|
import "./MarkdownContent.css";
|
||||||
|
import MarkdownView from "./MarkdownView";
|
||||||
|
import { useMarkdown } from "../../utils/queries/markdown-queries";
|
||||||
|
import { useMarkdownSetting } from "../../utils/queries/markdown-setting-queries";
|
||||||
|
import { useMarkdownTemplate } from "../../utils/queries/markdown-template-queries";
|
||||||
|
import { useMarkdownTemplateSetting } from "../../utils/queries/markdown-template-setting-queries";
|
||||||
|
import { useTree } from "../../utils/queries/tree-queries";
|
||||||
|
import { getMarkdownIdByPath } from "../../utils/pathUtils";
|
||||||
|
|
||||||
|
const StandaloneMarkdownPage = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const [indexTitle, setIndexTitle] = useState(null);
|
||||||
|
const [markdownId, setMarkdownId] = useState(null);
|
||||||
|
|
||||||
|
// Extract path from /pg/project/index -> project/index
|
||||||
|
const pathString = location.pathname.replace(/^\/pg\//, '');
|
||||||
|
|
||||||
|
const { data: tree, isLoading: isTreeLoading } = useTree();
|
||||||
|
const { data: markdown, isLoading: isMarkdownLoading, error } = useMarkdown(markdownId);
|
||||||
|
const { data: setting, isFetching: isSettingFetching } = useMarkdownSetting(markdown?.setting_id);
|
||||||
|
const { data: templateSetting, isFetching: isTemplateSettingFetching } = useMarkdownTemplateSetting(setting?.template_setting_id);
|
||||||
|
const { data: template, isFetching: isTemplateFetching } = useMarkdownTemplate(templateSetting?.template_id);
|
||||||
|
|
||||||
|
// Resolve markdown ID from path using tree
|
||||||
|
useEffect(() => {
|
||||||
|
if (tree && pathString) {
|
||||||
|
const resolvedId = getMarkdownIdByPath(tree, pathString);
|
||||||
|
setMarkdownId(resolvedId);
|
||||||
|
}
|
||||||
|
}, [tree, pathString]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (markdown && markdown.title === "index" && pathString) {
|
||||||
|
const pathParts = pathString.split('/').filter(part => part.length > 0);
|
||||||
|
|
||||||
|
if (pathParts.length === 0) {
|
||||||
|
// Root index: /pg/ or /pg
|
||||||
|
setIndexTitle("Home");
|
||||||
|
} else {
|
||||||
|
// Directory index: /pg/Projects or /pg/Projects/project1
|
||||||
|
// Use the last directory name as title
|
||||||
|
const directoryName = pathParts[pathParts.length - 1];
|
||||||
|
setIndexTitle(directoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [markdown, pathString]);
|
||||||
|
|
||||||
|
const notReady = isTreeLoading || isMarkdownLoading || isSettingFetching || isTemplateSettingFetching || isTemplateFetching;
|
||||||
|
|
||||||
|
if (notReady) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "2rem", textAlign: "center" }}>
|
||||||
|
<div>Loading...</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "2rem", textAlign: "center" }}>
|
||||||
|
<div>Error: {error.message || "Failed to load content"}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notReady && !markdownId) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "2rem", textAlign: "center" }}>
|
||||||
|
<div>Markdown not found for path: {pathString}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markdown?.isMessage) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "2rem" }}>
|
||||||
|
<div className="notification is-info">
|
||||||
|
<h4 className="title is-4">{markdown.title}</h4>
|
||||||
|
<p>{markdown.content}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "2rem", maxWidth: "100%", margin: "0 auto" }}>
|
||||||
|
<div style={{ marginBottom: "2rem" }}>
|
||||||
|
<h1 className="title">{markdown?.title === "index" ? indexTitle : markdown?.title}</h1>
|
||||||
|
</div>
|
||||||
|
{markdown && (
|
||||||
|
<MarkdownView content={JSON.parse(markdown.content)} template={template} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StandaloneMarkdownPage;
|
||||||
@@ -28,9 +28,10 @@ const TemplatedEditorComponent = ({ variable, value, namespace, onContentChanged
|
|||||||
<label className="label">{__namespace}</label>
|
<label className="label">{__namespace}</label>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<textarea
|
<textarea
|
||||||
className="textarea"
|
style={{maxHeight: "10vh"}}
|
||||||
value={value}
|
className="textarea"
|
||||||
onChange={(e) => onContentChanged(variable.name, e.target.value)}
|
value={value}
|
||||||
|
onChange={(e) => onContentChanged(variable.name, e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,7 +153,7 @@ const TemplatedEditorComponent = ({ variable, value, namespace, onContentChanged
|
|||||||
return <>{renderField()}</>;
|
return <>{renderField()}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TemplatedEditor = ({ content, template, onContentChanged }) => {
|
const TemplatedEditor = ({ content, template, onContentChanged, style }) => {
|
||||||
const tpl = template || {
|
const tpl = template || {
|
||||||
parameters: [{ name: "markdown", type: { base_type: "markdown", definition: {} } }],
|
parameters: [{ name: "markdown", type: { base_type: "markdown", definition: {} } }],
|
||||||
layout: "<markdown/>",
|
layout: "<markdown/>",
|
||||||
@@ -160,16 +161,27 @@ const TemplatedEditor = ({ content, template, onContentChanged }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="box">
|
<div className="box" style={{
|
||||||
{tpl.parameters.map((variable, idx) => (
|
...style,
|
||||||
<TemplatedEditorComponent
|
display: "flex",
|
||||||
key={idx}
|
flexDirection: "column",
|
||||||
variable={variable}
|
overflow: "hidden"
|
||||||
value={content[variable.name]}
|
}}>
|
||||||
namespace={tpl.title}
|
<div style={{
|
||||||
onContentChanged={onContentChanged}
|
flex: 1,
|
||||||
/>
|
overflowY: "auto",
|
||||||
))}
|
padding: "1rem"
|
||||||
|
}}>
|
||||||
|
{tpl.parameters.map((variable, idx) => (
|
||||||
|
<TemplatedEditorComponent
|
||||||
|
key={idx}
|
||||||
|
variable={variable}
|
||||||
|
value={content[variable.name]}
|
||||||
|
namespace={tpl.title}
|
||||||
|
onContentChanged={onContentChanged}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
42
src/components/Modals/JsonSchemaModal.js
Normal file
42
src/components/Modals/JsonSchemaModal.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const JsonSchemaModal = ({ isActive, onClose, schema }) => {
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(schema, null, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`modal ${isActive ? 'is-active' : ''}`}>
|
||||||
|
<div className="modal-background" onClick={onClose}></div>
|
||||||
|
<div className="modal-card">
|
||||||
|
<header className="modal-card-head">
|
||||||
|
<p className="modal-card-title">JSON Schema</p>
|
||||||
|
<button className="delete" aria-label="close" onClick={onClose}></button>
|
||||||
|
</header>
|
||||||
|
<section className="modal-card-body">
|
||||||
|
<div className="field">
|
||||||
|
<div className="control">
|
||||||
|
<textarea
|
||||||
|
className="textarea"
|
||||||
|
value={JSON.stringify(schema, null, 2)}
|
||||||
|
readOnly
|
||||||
|
style={{ height: "50vh" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer className="modal-card-foot">
|
||||||
|
<button className="button is-primary" onClick={handleCopy}>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-copy"></i>
|
||||||
|
</span>
|
||||||
|
<span>copy</span>
|
||||||
|
</button>
|
||||||
|
<button className="button" onClick={onClose}>close</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default JsonSchemaModal;
|
||||||
@@ -1,9 +1,30 @@
|
|||||||
import {Link} from "react-router-dom";
|
import {Link, useNavigate} from "react-router-dom";
|
||||||
import PermissionGuard from "../PermissionGuard";
|
import PermissionGuard from "../PermissionGuard";
|
||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import MarkdownSettingModal from "../Modals/MarkdownSettingModal";
|
import MarkdownSettingModal from "../Modals/MarkdownSettingModal";
|
||||||
|
import {useDeleteMarkdown} from "../../utils/queries/markdown-queries";
|
||||||
|
import {useDeleteMarkdownSetting} from "../../utils/queries/markdown-setting-queries";
|
||||||
const MarkdownNode = ({markdown, handleMoveMarkdown}) => {
|
const MarkdownNode = ({markdown, handleMoveMarkdown}) => {
|
||||||
const [isMarkdownSettingModalOpen, setIsMarkdownSettingModalOpen] = useState(false);
|
const [isMarkdownSettingModalOpen, setIsMarkdownSettingModalOpen] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const deleteMarkdown = useDeleteMarkdown();
|
||||||
|
const deleteMarkdownSetting = useDeleteMarkdownSetting();
|
||||||
|
|
||||||
|
const handleDeleteMarkdown = async () => {
|
||||||
|
if (!window.confirm(`delete markdown "${markdown.title}" ? this action cannot be undone.`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteMarkdown.mutateAsync(markdown.id);
|
||||||
|
|
||||||
|
if (window.location.pathname === `/markdown/${markdown.id}`) {
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('failed: ' + (error.message || 'unknown error'));
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<li key={markdown.id}>
|
<li key={markdown.id}>
|
||||||
<div className="is-clickable field has-addons">
|
<div className="is-clickable field has-addons">
|
||||||
@@ -24,6 +45,18 @@ const MarkdownNode = ({markdown, handleMoveMarkdown}) => {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
<p className="control">
|
||||||
|
<button
|
||||||
|
className="button is-small is-danger"
|
||||||
|
onClick={handleDeleteMarkdown}
|
||||||
|
type="button"
|
||||||
|
disabled={deleteMarkdown.isLoading || deleteMarkdownSetting.isLoading}
|
||||||
|
>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-trash"/>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
className="control is-flex is-flex-direction-column is-align-items-center"
|
className="control is-flex is-flex-direction-column is-align-items-center"
|
||||||
style={{marginLeft: "0.5rem"}}
|
style={{marginLeft: "0.5rem"}}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import React, { useState } from "react";
|
|||||||
import { useMarkdownTemplates } from "../../../utils/queries/markdown-template-queries";
|
import { useMarkdownTemplates } from "../../../utils/queries/markdown-template-queries";
|
||||||
import PermissionGuard from "../../PermissionGuard";
|
import PermissionGuard from "../../PermissionGuard";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import JsonSchemaModal from "../../Modals/JsonSchemaModal";
|
||||||
|
|
||||||
const TemplateTab = () => {
|
const TemplateTab = () => {
|
||||||
const { data: templates, isLoading, error } = useMarkdownTemplates();
|
const { data: templates, isLoading, error } = useMarkdownTemplates();
|
||||||
const [keyword, setKeyword] = useState("");
|
const [keyword, setKeyword] = useState("");
|
||||||
|
const [selectedSchema, setSelectedSchema] = useState(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const filteredTemplates = templates?.filter(template =>
|
const filteredTemplates = templates?.filter(template =>
|
||||||
@@ -16,6 +18,99 @@ const TemplateTab = () => {
|
|||||||
navigate(`/template/edit/${templateId}`);
|
navigate(`/template/edit/${templateId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateJsonSchema = (template) => {
|
||||||
|
const schema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
$defs: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateTypeSchema = (param, defName) => {
|
||||||
|
switch (param.type.base_type) {
|
||||||
|
case "string":
|
||||||
|
return {
|
||||||
|
type: "string"
|
||||||
|
};
|
||||||
|
case "markdown":
|
||||||
|
return {
|
||||||
|
type: "string",
|
||||||
|
description: "Markdown content"
|
||||||
|
};
|
||||||
|
case "enum":
|
||||||
|
return {
|
||||||
|
type: "string",
|
||||||
|
enum: param.type.definition.enums
|
||||||
|
};
|
||||||
|
case "list":
|
||||||
|
if (param.type.extend_type.base_type === "string") {
|
||||||
|
return {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (param.type.extend_type.base_type === "list" ||
|
||||||
|
param.type.extend_type.base_type === "template") {
|
||||||
|
const itemsDefName = `${defName}_items`;
|
||||||
|
schema.$defs[itemsDefName] = generateTypeSchema(
|
||||||
|
{ type: param.type.extend_type },
|
||||||
|
itemsDefName
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
$ref: `#/$defs/${itemsDefName}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: "array",
|
||||||
|
items: generateTypeSchema(
|
||||||
|
{ type: param.type.extend_type },
|
||||||
|
`${defName}_items`
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "template":
|
||||||
|
const nestedTemplate = templates.find(t => t.id === param.type.definition.template.id);
|
||||||
|
if (nestedTemplate) {
|
||||||
|
const nestedSchema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {}
|
||||||
|
};
|
||||||
|
nestedTemplate.parameters.forEach(nestedParam => {
|
||||||
|
nestedSchema.properties[nestedParam.name] = generateTypeSchema(
|
||||||
|
nestedParam,
|
||||||
|
`${defName}_${nestedParam.name}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return nestedSchema;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: "object",
|
||||||
|
properties: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
type: "object",
|
||||||
|
properties: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template.parameters.forEach(param => {
|
||||||
|
const defName = `param_${param.name}`;
|
||||||
|
schema.properties[param.name] = generateTypeSchema(param, defName);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Object.keys(schema.$defs).length === 0) {
|
||||||
|
delete schema.$defs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
};
|
||||||
|
|
||||||
if (isLoading) return <p>Loading...</p>;
|
if (isLoading) return <p>Loading...</p>;
|
||||||
if (error) return <p>Error loading templates</p>;
|
if (error) return <p>Error loading templates</p>;
|
||||||
|
|
||||||
@@ -38,23 +133,40 @@ const TemplateTab = () => {
|
|||||||
Create New Template
|
Create New Template
|
||||||
</a>
|
</a>
|
||||||
</PermissionGuard>
|
</PermissionGuard>
|
||||||
|
<ul className="menu-list">
|
||||||
{!filteredTemplates || filteredTemplates.length === 0 ? (
|
{filteredTemplates?.map((template) => (
|
||||||
<p>No templates found</p>
|
<li key={template.id}>
|
||||||
) : (
|
<div className="is-flex is-justify-content-space-between is-align-items-center">
|
||||||
<div className="template-list">
|
<span>{template.title}</span>
|
||||||
{filteredTemplates.map(template => (
|
<div className="field has-addons is-justify-content-flex-end">
|
||||||
<button
|
<button
|
||||||
key={template.id}
|
className="button is-small control"
|
||||||
className="button is-light is-fullwidth template-button"
|
onClick={() => handleTemplateClick(template.id)}
|
||||||
onClick={() => handleTemplateClick(template.id)}
|
type="button"
|
||||||
style={{ marginBottom: "5px", textAlign: "left", justifyContent: "flex-start" }}
|
>
|
||||||
>
|
<span className="icon">
|
||||||
{template.title}
|
<i className="fas fa-edit"></i>
|
||||||
</button>
|
</span>
|
||||||
))}
|
</button>
|
||||||
</div>
|
<button
|
||||||
)}
|
className="button is-small control"
|
||||||
|
onClick={() => setSelectedSchema(generateJsonSchema(template))}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-code"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<JsonSchemaModal
|
||||||
|
isActive={selectedSchema !== null}
|
||||||
|
onClose={() => setSelectedSchema(null)}
|
||||||
|
schema={selectedSchema}
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
|||||||
let current_id = pathId;
|
let current_id = pathId;
|
||||||
while (current_id) {
|
while (current_id) {
|
||||||
try {
|
try {
|
||||||
const pathData = await queryClient.fetchQuery(
|
const pathData = await queryClient.fetchQuery({
|
||||||
["path", current_id],
|
queryKey: ["path", current_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/path/${current_id}`)
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/path/${current_id}`)
|
||||||
);
|
});
|
||||||
if (!pathData) break;
|
if (!pathData) break;
|
||||||
path.unshift({ name: pathData.name, id: pathData.id });
|
path.unshift({ name: pathData.name, id: pathData.id });
|
||||||
current_id = pathData.parent_id;
|
current_id = pathData.parent_id;
|
||||||
@@ -71,8 +71,8 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
|||||||
{ name: searchTerm.trim(), parent_id: currentPathId },
|
{ name: searchTerm.trim(), parent_id: currentPathId },
|
||||||
{
|
{
|
||||||
onSuccess: (newDir) => {
|
onSuccess: (newDir) => {
|
||||||
queryClient.setQueryData(["path", newDir.id], newDir);
|
queryClient.setQueryData({queryKey: ["path", newDir.id]}, newDir);
|
||||||
queryClient.invalidateQueries(["paths", currentPathId]);
|
queryClient.invalidateQueries({queryKey: ["paths", currentPathId]});
|
||||||
setSearchTerm("");
|
setSearchTerm("");
|
||||||
alert("Directory created successfully.");
|
alert("Directory created successfully.");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import AuthProvider, {AuthContext} from "./AuthProvider";
|
|||||||
import "bulma/css/bulma.min.css";
|
import "bulma/css/bulma.min.css";
|
||||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query"
|
import {QueryClient, QueryClientProvider} from "@tanstack/react-query"
|
||||||
import ConfigProvider from "./ConfigProvider";
|
import ConfigProvider from "./ConfigProvider";
|
||||||
|
import ControlledReactQueryDevtools from "./components/Debug/ControlledReactQueryDevtools";
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
@@ -52,6 +53,7 @@ root.render(
|
|||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<EnhancedAuthProvider>
|
<EnhancedAuthProvider>
|
||||||
<App />
|
<App />
|
||||||
|
<ControlledReactQueryDevtools />
|
||||||
</EnhancedAuthProvider>
|
</EnhancedAuthProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
45
src/utils/pathUtils.js
Normal file
45
src/utils/pathUtils.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export const findMarkdownByPath = (tree, pathString) => {
|
||||||
|
if (!tree || !pathString) return null;
|
||||||
|
|
||||||
|
const pathSegments = pathString.split('/').filter(segment => segment.length > 0);
|
||||||
|
|
||||||
|
if (pathSegments.length === 0) {
|
||||||
|
const rootIndex = tree.children?.find(
|
||||||
|
child => child.type === 'markdown' && child.title === 'index'
|
||||||
|
);
|
||||||
|
return rootIndex || null;
|
||||||
|
}
|
||||||
|
let currentNode = tree;
|
||||||
|
|
||||||
|
for (let i = 0; i < pathSegments.length; i++) {
|
||||||
|
const segment = pathSegments[i];
|
||||||
|
|
||||||
|
const childPath = currentNode.children?.find(
|
||||||
|
child => child.type === 'path' && child.name === segment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!childPath) {
|
||||||
|
if (i === pathSegments.length - 1) {
|
||||||
|
const markdownNode = currentNode.children?.find(
|
||||||
|
child => child.type === 'markdown' && child.title === segment
|
||||||
|
);
|
||||||
|
return markdownNode || null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = childPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const indexMarkdown = currentNode.children?.find(
|
||||||
|
child => child.type === 'markdown' && child.title === 'index'
|
||||||
|
);
|
||||||
|
|
||||||
|
return indexMarkdown || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMarkdownIdByPath = (tree, pathString) => {
|
||||||
|
const markdownNode = findMarkdownByPath(tree, pathString);
|
||||||
|
return markdownNode?.id || null;
|
||||||
|
};
|
||||||
@@ -5,13 +5,13 @@ import {fetch_} from "../request-utils";
|
|||||||
export const useMarkdownPermissionSettings = () => {
|
export const useMarkdownPermissionSettings = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_permission_settings"],
|
queryKey: ["markdown_permission_settings"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/`),
|
||||||
onSuccess: (data) => {
|
onSuccess: async (data) => {
|
||||||
if(data){
|
if (data) {
|
||||||
for(const setting of data){
|
for (const setting of data) {
|
||||||
queryClient.invalidateQueries(["markdown_permission_setting", setting.id]);
|
await queryClient.invalidateQueries(["markdown_permission_setting", setting.id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,9 +21,9 @@ export const useMarkdownPermissionSettings = () => {
|
|||||||
|
|
||||||
export const useMarkdownPermissionSetting = (setting_id) => {
|
export const useMarkdownPermissionSetting = (setting_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_permission_setting", setting_id],
|
queryKey: ["markdown_permission_setting", setting_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/${setting_id}/`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/${setting_id}`),
|
||||||
enabled: !!setting_id,
|
enabled: !!setting_id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -32,15 +32,17 @@ export const useMarkdownPermissionSetting = (setting_id) => {
|
|||||||
export const useCreateMarkdownPermissionSetting = () => {
|
export const useCreateMarkdownPermissionSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation((data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/`, {
|
return useMutation(
|
||||||
method: "POST",
|
{
|
||||||
body: JSON.stringify(data),
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/permission/`, {
|
||||||
}), {
|
method: "POST",
|
||||||
onSuccess: (data) => {
|
body: JSON.stringify(data),
|
||||||
queryClient.invalidateQueries(["markdown_permission_setting", data.id]);
|
}),
|
||||||
queryClient.invalidateQueries(["markdown_permission_settings"]);
|
onSuccess: async (data) => {
|
||||||
}
|
await queryClient.invalidateQueries(["markdown_permission_setting", data.id]);
|
||||||
});
|
await queryClient.invalidateQueries(["markdown_permission_settings"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateMarkdownPermissionSetting = () => {
|
export const useUpdateMarkdownPermissionSetting = () => {
|
||||||
|
|||||||
@@ -6,71 +6,73 @@ import {useConfig} from "../../ConfigProvider";
|
|||||||
|
|
||||||
export const useMarkdown = (id) => {
|
export const useMarkdown = (id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown", id],
|
queryKey: ["markdown", id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/${id}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/markdown/${id}`),
|
||||||
{
|
enabled: !!id,
|
||||||
enabled: !!id,
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useIndexMarkdown = (path_id) => {
|
export const useIndexMarkdown = (path_id) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["index_markdown", path_id],
|
queryKey: ["index_markdown", path_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/get_index/${path_id}`),{
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/markdown/get_index/${path_id}`),
|
||||||
enabled: !!path_id,
|
enabled: !!path_id,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if(data && data.id){
|
if(data && data.id){
|
||||||
queryClient.setQueryData(["markdown", data.id], data);
|
queryClient.setQueryData({queryKey: ["markdown", data.id]}, data);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useHomeMarkdown = () => {
|
export const useHomeMarkdown = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["home_markdown"],
|
queryKey: ["home_markdown"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/get_home`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/markdown/get_home`),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data && data.id){
|
if (data && data.id){
|
||||||
queryClient.setQueryData(["markdown", data.id], data);
|
queryClient.setQueryData({queryKey: ["markdown", data.id]}, data);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useMarkdownsByPath = (pathId) => {
|
export const useMarkdownsByPath = (pathId) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdownsByPath", pathId],
|
queryKey: ["markdownsByPath", pathId],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/by_path/${pathId}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/markdown/by_path/${pathId}`),
|
||||||
{
|
enabled: !!pathId
|
||||||
enabled: !!pathId
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useSaveMarkdown = () => {
|
export const useSaveMarkdown = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useMutation(({id, data}) => {
|
return useMutation({
|
||||||
const url = id
|
mutationFn: ({id, data}) => {
|
||||||
? `${config.BACKEND_HOST}/api/markdown/${id}`
|
const url = id
|
||||||
: `${config.BACKEND_HOST}/api/markdown/`;
|
? `${config.BACKEND_HOST}/api/markdown/${id}`
|
||||||
const method = id ? "PATCH" : "POST";
|
: `${config.BACKEND_HOST}/api/markdown/`;
|
||||||
return fetch_(url, {
|
const method = id ? "PATCH" : "POST";
|
||||||
method,
|
return fetch_(url, {
|
||||||
body: JSON.stringify(data),
|
method,
|
||||||
})
|
body: JSON.stringify(data),
|
||||||
},{
|
});
|
||||||
onSuccess: (res) => {
|
},
|
||||||
queryClient.invalidateQueries(["markdownsByPath", res.path_id]);
|
onSuccess: async (res) => {
|
||||||
queryClient.invalidateQueries(["markdown", res.id]);
|
await queryClient.invalidateQueries({queryKey: ["markdownsByPath", res.path_id]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
await queryClient.invalidateQueries({queryKey: ["markdown", res.id]});
|
||||||
|
await queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
|
|
||||||
|
console.log("invalidateQueries: ", res.id, typeof res.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -80,34 +82,52 @@ export const useMoveMarkdown = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({markdown, direction}) => {
|
mutationFn: ({markdown, direction}) => {
|
||||||
const apiEndpoint = `${config.BACKEND_HOST}/api/markdown/move_${direction}/${markdown.id}`;
|
const apiEndpoint = `${config.BACKEND_HOST}/api/markdown/move_${direction}/${markdown.id}`;
|
||||||
return fetch_(apiEndpoint, {method: "PATCH"});
|
return fetch_(apiEndpoint, {method: "PATCH"});
|
||||||
},
|
},
|
||||||
{
|
onSuccess: () => {
|
||||||
onSuccess: () => {
|
queryClient.invalidateQueries({queryKey: ["paths"]});
|
||||||
queryClient.invalidateQueries(["paths"]);
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteMarkdown = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (markdownId) => {
|
||||||
|
return fetch_(`${config.BACKEND_HOST}/api/markdown/${markdownId}`, {
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (data, markdownId) => {
|
||||||
|
queryClient.invalidateQueries({queryKey: ["markdown", markdownId]});
|
||||||
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
|
queryClient.invalidateQueries({queryKey: ["markdownsByPath"]});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSearchMarkdown = (keyword) => {
|
export const useSearchMarkdown = (keyword) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(["markdownsByKeyword", keyword],
|
return useQuery({
|
||||||
() => fetch_(
|
queryKey: ["markdownsByKeyword", keyword],
|
||||||
|
queryFn: () => fetch_(
|
||||||
`${config.BACKEND_HOST}/api/markdown/search/${encodeURIComponent(keyword)}`,
|
`${config.BACKEND_HOST}/api/markdown/search/${encodeURIComponent(keyword)}`,
|
||||||
),
|
),
|
||||||
{
|
enabled: !!keyword,
|
||||||
enabled: !!keyword,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useLinks = () => {
|
export const useLinks = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(["links"], () => fetch_(`${config.BACKEND_HOST}/api/markdown/links`));
|
return useQuery({
|
||||||
|
queryKey: ["links"],
|
||||||
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/markdown/links`)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,71 +5,66 @@ import {fetch_} from "../request-utils";
|
|||||||
export const useMarkdownSettings = () => {
|
export const useMarkdownSettings = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_setting"],
|
queryKey: ["markdown_setting"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`),
|
||||||
{
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
if(data){
|
||||||
if(data){
|
for(const setting of data)
|
||||||
for(const setting of data)
|
queryClient.setQueryData({queryKey: ["markdown_setting", setting.id]}, setting);
|
||||||
queryClient.invalidateQueries(["markdown_setting", setting.id]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useMarkdownSetting = (setting_id) => {
|
export const useMarkdownSetting = (setting_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_setting", setting_id],
|
queryKey: ["markdown_setting", setting_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${setting_id}`, {}), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${setting_id}`, {}),
|
||||||
enabled: !!setting_id,
|
enabled: !!setting_id,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useCreateMarkdownSetting = () => {
|
export const useCreateMarkdownSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}), {
|
}),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["markdown_setting", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_setting", data.id]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useUpdateMarkdownSetting = () => {
|
export const useUpdateMarkdownSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}),{
|
}),
|
||||||
onSuccess: (data, variables) => {
|
onSuccess: (data, variables) => {
|
||||||
queryClient.invalidateQueries(["markdown_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_setting", variables.id]});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteMarkdownSetting = () => {
|
export const useDeleteMarkdownSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}),{
|
}),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["markdown_setting", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_setting", data.id]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,95 +7,90 @@ import {data} from "react-router-dom";
|
|||||||
|
|
||||||
export const useMarkdownTemplate = (template_id) => {
|
export const useMarkdownTemplate = (template_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_template", template_id],
|
queryKey: ["markdown_template", template_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${template_id}`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${template_id}`),
|
||||||
enabled: !!template_id,
|
enabled: !!template_id,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useMarkdownTemplates = () => {
|
export const useMarkdownTemplates = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_templates"],
|
queryKey: ["markdown_templates"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if(data){
|
if(data){
|
||||||
for(const template of data){
|
for(const template of data){
|
||||||
queryClient.setQueryData(["markdown_template", template.id], template);
|
queryClient.setQueryData({queryKey: ["markdown_template", template.id]}, template);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateMarkdownTemplate = () => {
|
export const useUpdateMarkdownTemplate = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
queryClient.invalidateQueries({queryKey: ["markdown_template", data.id]});
|
||||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_templates"]});
|
||||||
queryClient.invalidateQueries(["markdown_templates"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCreateMarkdownTemplate = () => {
|
export const useCreateMarkdownTemplate = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}),{
|
}),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template", data.id]});
|
||||||
queryClient.invalidateQueries(["markdown_templates"]);
|
queryClient.invalidateQueries({queryKey: ["markdown_templates"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDeleteMarkdownTemplate = () => {
|
export const useDeleteMarkdownTemplate = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
||||||
method: "DELETE"
|
method: "DELETE"
|
||||||
}), {
|
}),
|
||||||
onSuccess: (res, variables) => {
|
onSuccess: (res, variables) => {
|
||||||
queryClient.invalidateQueries(["markdown_template", variables]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template", variables]});
|
||||||
queryClient.invalidateQueries(["markdown_templates"]);
|
queryClient.invalidateQueries({queryKey: ["markdown_templates"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSaveMarkdownTemplate = () => {
|
export const useSaveMarkdownTemplate = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(({id, data}) => {
|
return useMutation({
|
||||||
const url = id
|
mutationFn: ({id, data}) => {
|
||||||
? `${config.BACKEND_HOST}/api/template/markdown/${id}`
|
const url = id
|
||||||
: `${config.BACKEND_HOST}/api/template/markdown/`;
|
? `${config.BACKEND_HOST}/api/template/markdown/${id}`
|
||||||
const method = id? "PUT": "POST";
|
: `${config.BACKEND_HOST}/api/template/markdown/`;
|
||||||
return fetch_(url, {
|
const method = id? "PUT": "POST";
|
||||||
method,
|
return fetch_(url, {
|
||||||
body: JSON.stringify(data),
|
method,
|
||||||
})
|
body: JSON.stringify(data),
|
||||||
},{
|
});
|
||||||
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template", data.id]});
|
||||||
queryClient.invalidateQueries(["markdown_templates"]);
|
queryClient.invalidateQueries({queryKey: ["markdown_templates"]});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,40 +5,39 @@ import {fetch_} from "../request-utils";
|
|||||||
export const useMarkdownTemplateSettings = () => {
|
export const useMarkdownTemplateSettings = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_template_settings"],
|
queryKey: ["markdown_template_settings"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/`),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if(data){
|
if(data){
|
||||||
for(const setting of data){
|
for(const setting of data){
|
||||||
queryClient.invalidateQueries(["markdown_template_setting", settings.id]);
|
queryClient.setQueryData({queryKey: ["markdown_template_setting", setting.id]}, setting);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useMarkdownTemplateSetting = (setting_id) => {
|
export const useMarkdownTemplateSetting = (setting_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["markdown_template_setting", setting_id],
|
queryKey: ["markdown_template_setting", setting_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${setting_id}`), {
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${setting_id}`),
|
||||||
enabled: !!setting_id,
|
enabled: !!setting_id,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useCreateMarkdownTemplateSetting = () => {
|
export const useCreateMarkdownTemplateSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation((data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/`, {
|
return useMutation({
|
||||||
method: "POST",
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/`, {
|
||||||
body: JSON.stringify(data),
|
method: "POST",
|
||||||
}), {
|
body: JSON.stringify(data),
|
||||||
|
}),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["markdown_template_setting", data.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template_setting", data.id]});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -46,29 +45,27 @@ export const useCreateMarkdownTemplateSetting = () => {
|
|||||||
export const useUpdateMarkdownTemplateSetting = () => {
|
export const useUpdateMarkdownTemplateSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}),{
|
}),
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
queryClient.invalidateQueries(["markdown_template_setting", res.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template_setting", res.id]});
|
||||||
queryClient.invalidateQueries(["markdown_template_settings"]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template_settings"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteMarkdownTemplateSetting = () => {
|
export const useDeleteMarkdownTemplateSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
mutationFn: ({id}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}), {
|
}),
|
||||||
onSuccess: (res, variables) => {
|
onSuccess: (res, variables) => {
|
||||||
queryClient.invalidateQueries(["markdown_template_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["markdown_template_setting", variables.id]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,87 +6,77 @@ import {useConfig} from "../../ConfigProvider";
|
|||||||
export const usePaths = (parent_id) => {
|
export const usePaths = (parent_id) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["paths", parent_id],
|
queryKey: ["paths", parent_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/path/parent/${parent_id}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/path/parent/${parent_id}`),
|
||||||
{
|
enabled: !!parent_id,
|
||||||
enabled: !!parent_id,
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
if(data) {
|
||||||
if(data) {
|
for (const pth of data)
|
||||||
for (const pth of data)
|
{
|
||||||
{
|
queryClient.setQueryData({queryKey: ["path", pth.id]}, pth);
|
||||||
queryClient.setQueryData(["path", pth.id], pth);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const usePath = (id) => {
|
export const usePath = (id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["path", id],
|
queryKey: ["path", id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/path/${id}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/path/${id}`),
|
||||||
{
|
enabled: !!id
|
||||||
enabled: !!id
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCreatePath = () => {
|
export const useCreatePath = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/path/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/path/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (res) => {
|
||||||
onSuccess: (res) => {
|
queryClient.invalidateQueries({queryKey: ["paths", res.parent_id]});
|
||||||
queryClient.invalidateQueries(["paths", res.parent_id]);
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
},
|
||||||
},
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdatePath = () => {
|
export const useUpdatePath = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({ id, data }) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
mutationFn: ({ id, data }) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (res) => {
|
||||||
onSuccess: (res) => {
|
queryClient.invalidateQueries({queryKey: ["paths", res.parent_id]});
|
||||||
queryClient.invalidateQueries(["paths", res.parent_id]);
|
queryClient.invalidateQueries({queryKey: ["path", res.id]});
|
||||||
queryClient.invalidateQueries(["path", res.id]);
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
},
|
||||||
},
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeletePath = () => {
|
export const useDeletePath = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: () => {
|
||||||
onSuccess: () => {
|
queryClient.invalidateQueries({queryKey: ["paths"]});
|
||||||
queryClient.invalidateQueries(["paths"]);
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
},
|
||||||
},
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -94,16 +84,14 @@ export const useMovePath = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({path, direction}) => {
|
mutationFn: ({path, direction}) => {
|
||||||
const apiEndpoint = `${config.BACKEND_HOST}/api/path/move_${direction}/${path.id}`;
|
const apiEndpoint = `${config.BACKEND_HOST}/api/path/move_${direction}/${path.id}`;
|
||||||
return fetch_(apiEndpoint, {method: "PATCH"});
|
return fetch_(apiEndpoint, {method: "PATCH"});
|
||||||
},
|
},
|
||||||
{
|
onSuccess: () => {
|
||||||
onSuccess: () => {
|
queryClient.invalidateQueries({queryKey: ["paths"]});
|
||||||
queryClient.invalidateQueries(["paths"]);
|
queryClient.invalidateQueries({queryKey: ["tree"]});
|
||||||
queryClient.invalidateQueries(["tree"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,76 +5,69 @@ import {fetch_} from "../request-utils";
|
|||||||
export const usePathSettings = () => {
|
export const usePathSettings = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
"path_settings",
|
queryKey: ["path_settings"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/path/`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/path/`),
|
||||||
{
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
if(data){
|
||||||
if(data){
|
for(const setting of data)
|
||||||
for(const setting of data)
|
queryClient.setQueryData({queryKey: ["path_setting", setting.id]}, setting);
|
||||||
queryClient.setQueryData(["path_setting", setting.id], setting);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePathSetting = (setting_id) => {
|
export const usePathSetting = (setting_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["path_setting", setting_id],
|
queryKey: ["path_setting", setting_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/path/${setting_id}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/path/${setting_id}`),
|
||||||
{
|
enabled: !!setting_id,
|
||||||
enabled: !!setting_id,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCreatePathSetting = () => {
|
export const useCreatePathSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/setting/path/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/setting/path/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}), {
|
}),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.invalidateQueries(["path_setting", data.id]);
|
queryClient.invalidateQueries({queryKey: ["path_setting", data.id]});
|
||||||
queryClient.invalidateQueries(["path_settings"]);
|
queryClient.invalidateQueries({queryKey: ["path_settings"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useUpdatePathSetting = () => {
|
export const useUpdatePathSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}), {
|
}),
|
||||||
onSuccess: (data, variables) => {
|
onSuccess: (data, variables) => {
|
||||||
queryClient.invalidateQueries(["path_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["path_setting", variables.id]});
|
||||||
queryClient.invalidateQueries(["path_settings"]);
|
queryClient.invalidateQueries({queryKey: ["path_settings"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useDeletePathSetting = () => {
|
export const useDeletePathSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}),{
|
}),
|
||||||
onSuccess: (data, variables) => {
|
onSuccess: (data, variables) => {
|
||||||
queryClient.invalidateQueries(["path_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["path_setting", variables.id]});
|
||||||
queryClient.invalidateQueries(["path_settings"]);
|
queryClient.invalidateQueries({queryKey: ["path_settings"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import {useConfig} from "../../ConfigProvider";
|
|||||||
export const useTree = () => {
|
export const useTree = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["tree"],
|
queryKey: ["tree"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/tree/`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/tree/`),
|
||||||
{
|
onSuccess: data => {
|
||||||
onSuccess: data => {
|
if(data)
|
||||||
if(data)
|
queryClient.setQueryData({queryKey: ["tree"]}, data);
|
||||||
queryClient.setQueryData(["tree"], data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -5,147 +5,132 @@ import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
|||||||
export const useWebhooks = () =>{
|
export const useWebhooks = () =>{
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["webhooks"],
|
queryKey: ["webhooks"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/webhook/`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/webhook/`),
|
||||||
{
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
if(data){
|
||||||
if(data){
|
for(const webhook of data){
|
||||||
for(const webhook of data){
|
queryClient.setQueryData({queryKey: ["webhook", data.id]}, data);
|
||||||
queryClient.setQueryData(["webhook", data.id], data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCreateWebhook = () => {
|
export const useCreateWebhook = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/webhook/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/webhook/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"hook_url": data
|
"hook_url": data
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: () => {
|
||||||
onSuccess: () => {
|
queryClient.invalidateQueries({queryKey: ["webhooks"]});
|
||||||
queryClient.invalidateQueries(["webhooks"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateWebhook = () =>{
|
export const useUpdateWebhook = () =>{
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/webhook/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/webhook/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (res) => {
|
||||||
onSuccess: (res) => {
|
queryClient.invalidateQueries({queryKey: ["webhook", res.id]});
|
||||||
queryClient.invalidateQueries(["webhook", res.id]);
|
queryClient.invalidateQueries({queryKey: ["webhooks"]});
|
||||||
queryClient.invalidateQueries(["webhooks"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteWebhook = () => {
|
export const useDeleteWebhook = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/webhook/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/webhook/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (res, variables) => {
|
||||||
onSuccess: (res, variables) => {
|
queryClient.invalidateQueries({queryKey: ["webhook", variables.id]});
|
||||||
queryClient.invalidateQueries(["webhook", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["webhooks"]});
|
||||||
queryClient.invalidateQueries(["webhooks"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useWebhookSettings = () => {
|
export const useWebhookSettings = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["webhook_setting"],
|
queryKey: ["webhook_setting"],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/`),
|
||||||
{
|
onSuccess: (data) => {
|
||||||
onSuccess: (data) => {
|
if(data){
|
||||||
if(data){
|
for(const setting of data){
|
||||||
for(const setting of data){
|
queryClient.setQueryData({queryKey: ["webhook_setting", setting.id]}, setting);
|
||||||
queryClient.setQueryData(["webhook_setting", setting.id], setting);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useWebhookSetting = (setting_id) => {
|
export const useWebhookSetting = (setting_id) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return useQuery(
|
return useQuery({
|
||||||
["webhook_setting", setting_id],
|
queryKey: ["webhook_setting", setting_id],
|
||||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${setting_id}`),
|
queryFn: () => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${setting_id}`),
|
||||||
{
|
enabled: !!setting_id,
|
||||||
enabled: !!setting_id,
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useCreateWebhookSetting = () => {
|
export const useCreateWebhookSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(data) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/`, {
|
mutationFn: (data) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}),{
|
}),
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
queryClient.invalidateQueries(["webhook_setting", res.id]);
|
queryClient.invalidateQueries({queryKey: ["webhook_setting", res.id]});
|
||||||
queryClient.invalidateQueries(["webhook_setting"]);
|
queryClient.invalidateQueries({queryKey: ["webhook_setting"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateWebhookSetting = () => {
|
export const useUpdateWebhookSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${id}`, {
|
mutationFn: ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
}),{
|
}),
|
||||||
onSuccess: (res, variables) => {
|
onSuccess: (res, variables) => {
|
||||||
queryClient.invalidateQueries(["webhook_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["webhook_setting", variables.id]});
|
||||||
queryClient.invalidateQueries(["webhook_setting"]);
|
queryClient.invalidateQueries({queryKey: ["webhook_setting"]});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useDeleteWebhookSetting = () => {
|
export const useDeleteWebhookSetting = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(id) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${id}`, {
|
mutationFn: (id) => fetch_(`${config.BACKEND_HOST}/api/setting/path/webhook/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}),
|
}),
|
||||||
{
|
onSuccess: (res, variables) => {
|
||||||
onSuccess: (res, variables) => {
|
queryClient.invalidateQueries({queryKey: ["webhook_setting", variables.id]});
|
||||||
queryClient.invalidateQueries(["webhook_setting", variables.id]);
|
queryClient.invalidateQueries({queryKey: ["webhook_setting"]});
|
||||||
queryClient.invalidateQueries(["webhook_setting"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user