);
+
};
export default PathSettingModal;
\ No newline at end of file
diff --git a/src/components/PathManager.js b/src/components/PathManager.js
index 7d84dce..d0e7981 100644
--- a/src/components/PathManager.js
+++ b/src/components/PathManager.js
@@ -1,6 +1,6 @@
import React, {useEffect, useState, useRef, useContext} from "react";
-import { useCreatePath, usePaths } from "../utils/path-queries";
+import {useCreatePath, usePath, usePaths} from "../utils/path-queries";
import { useQueryClient } from "react-query";
import "./PathManager.css";
import {fetch_} from "../utils/request-utils";
@@ -9,7 +9,8 @@ import PathSettingModal from "./Modals/PathSettingModal";
const PathManager = ({ currentPathId = 1, onPathChange }) => {
- const [currentPath, setCurrentPath] = useState([{ name: "Root", id: 1 }]);
+ const { data: currentPath } = usePath(currentPathId);
+ const [currentFullPath, setCurrentFullPath] = useState([{ name: "Root", id: 1 }]);
const [searchTerm, setSearchTerm] = useState("");
const [dropdownActive, setDropdownActive] = useState(false);
@@ -25,7 +26,7 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
setIsPathSettingModalModalOpen(true);
}
- const buildPath = async (pathId) => {
+ const buildFullPath = async (pathId) => {
const path = [];
let current_id = pathId;
while (current_id) {
@@ -47,21 +48,21 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
useEffect(() => {
const init = async () => {
- const path = await buildPath(currentPathId);
- setCurrentPath(path);
+ const path = await buildFullPath(currentPathId);
+ setCurrentFullPath(path);
};
init();
}, [currentPathId, queryClient]);
const handlePathClick = (pathId, pathIndex) => {
- const newPath = currentPath.slice(0, pathIndex + 1);
- setCurrentPath(newPath);
+ const newPath = currentFullPath.slice(0, pathIndex + 1);
+ setCurrentFullPath(newPath);
onPathChange(pathId);
};
const handleSubPathSelect = (subPath) => {
- const updatedPath = [...currentPath, { name: subPath.name, id: subPath.id }];
- setCurrentPath(updatedPath);
+ const updatedPath = [...currentFullPath, { name: subPath.name, id: subPath.id }];
+ setCurrentFullPath(updatedPath);
onPathChange(subPath.id);
setSearchTerm("");
setDropdownActive(false);
@@ -113,14 +114,14 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
- {currentPath.map((path, index) => (
+ {currentFullPath.map((path, index) => (
handlePathClick(path.id, index)}
>
{path.name}
- {index < currentPath.length - 1 && " / "}
+ {index < currentFullPath.length - 1 && " / "}
))}
@@ -137,7 +138,7 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
setIsPathSettingModalModalOpen(false)}
/>
diff --git a/src/components/Settings/PathSettings/WebhookSettingPanel.js b/src/components/Settings/PathSettings/WebhookSettingPanel.js
new file mode 100644
index 0000000..c8c6e59
--- /dev/null
+++ b/src/components/Settings/PathSettings/WebhookSettingPanel.js
@@ -0,0 +1,433 @@
+import React, { useEffect, useState } from "react";
+import {
+ useWebhookSetting,
+ useCreateWebhookSetting,
+ useUpdateWebhookSetting,
+ useWebhooks,
+} from "../../../utils/webhook-queries";
+
+import {
+ useCreateWebhook,
+ useUpdateWebhook,
+ useDeleteWebhook,
+} from "../../../utils/webhook-queries";
+import {useUpdatePathSetting} from "../../../utils/setting-queries";
+
+const WebhookSettingPanel = ({pathSetting, onClose}) => {
+
+ const {data: setting} = useWebhookSetting(pathSetting?.webhook_setting_id || 0);
+ const {data: webhooks, isLoading: isWebhooksLoading} = useWebhooks();
+
+ const createWebhookSetting = useCreateWebhookSetting();
+ const updateWebhookSetting = useUpdateWebhookSetting();
+
+ const createWebhook = useCreateWebhook();
+ const updateWebhook = useUpdateWebhook();
+ const deleteWebhook = useDeleteWebhook();
+
+ const updatePathSetting = useUpdatePathSetting();
+
+
+ const [enabled, setEnabled] = useState(false);
+ const [isRecursive, setIsRecursive] = useState(false);
+ const [onEvents, setOnEvents] = useState(0);
+ const [selectedUrl, setSelectedUrl] = useState("");
+ const [headerList, setHeaderList] = useState([]);
+ const [additionalHeaders, setAdditionalHeaders] = useState({});
+
+ const [isOnMarkdownCreated, setIsOnMarkdownCreated] = useState(false);
+ const [isOnMarkdownUpdated, setIsOnMarkdownUpdated] = useState(false);
+ const [isOnMarkdownDeleted, setIsOnMarkdownDeleted] = useState(false);
+ const [isOnPathCreated, setIsOnPathCreated] = useState(false);
+ const [isOnPathUpdated, setIsOnPathUpdated] = useState(false);
+ const [isOnPathDeleted, setIsOnPathDeleted] = useState(false);
+
+ const assignFromOnEvents = (bits) => {
+ setIsOnMarkdownCreated(!!(bits & 1));
+ setIsOnMarkdownUpdated(!!(bits & 2));
+ setIsOnMarkdownDeleted(!!(bits & 4));
+ setIsOnPathCreated(!!(bits & 8));
+ setIsOnPathUpdated(!!(bits & 16));
+ setIsOnPathDeleted(!!(bits & 32));
+ };
+
+ const handleTriggerEventsUpdate = (eventType, isChecked) => {
+ setOnEvents((prev) => {
+ let nextVal = prev;
+ switch (eventType) {
+ case "MARKDOWN_CREATED":
+ nextVal = isChecked ? nextVal | 1 : nextVal & ~1;
+ setIsOnMarkdownCreated(isChecked);
+ break;
+ case "MARKDOWN_UPDATED":
+ nextVal = isChecked ? nextVal | 2 : nextVal & ~2;
+ setIsOnMarkdownUpdated(isChecked);
+ break;
+ case "MARKDOWN_DELETED":
+ nextVal = isChecked ? nextVal | 4 : nextVal & ~4;
+ setIsOnMarkdownDeleted(isChecked);
+ break;
+ case "PATH_CREATED":
+ nextVal = isChecked ? nextVal | 8 : nextVal & ~8;
+ setIsOnPathCreated(isChecked);
+ break;
+ case "PATH_UPDATED":
+ nextVal = isChecked ? nextVal | 16 : nextVal & ~16;
+ setIsOnPathUpdated(isChecked);
+ break;
+ case "PATH_DELETED":
+ nextVal = isChecked ? nextVal | 32 : nextVal & ~32;
+ setIsOnPathUpdated(isChecked);
+ break;
+ default:
+ break;
+ }
+ return nextVal;
+ });
+ };
+
+ const handleCreateWebhookSetting = () => {
+ createWebhookSetting.mutate({}, {
+ onSuccess: (data) => {
+ updatePathSetting.mutate({id: pathSetting.id, data: {webhook_setting_id: data.id}});
+ }
+ });
+ };
+
+ useEffect(() => {
+ if (setting && webhooks) {
+ setEnabled(setting.enabled);
+ setIsRecursive(setting.recursive);
+ setOnEvents(setting.on_events);
+ assignFromOnEvents(setting.on_events);
+
+ try {
+ const headers = setting.additional_header
+ ? JSON.parse(setting.additional_header)
+ : {};
+ setAdditionalHeaders(headers);
+ setHeaderList(
+ Object.entries(headers).map(([k, v]) => ({ key: k, value: v }))
+ );
+ } catch (err) {
+ setAdditionalHeaders({});
+ setHeaderList([]);
+ }
+
+ const found = webhooks.find((wh) => wh.id === setting.webhook_id);
+ console.log("found", found);
+ console.log("webhooks", webhooks);
+ console.log("setting.webhook_id", setting.webhook_id);
+ setSelectedUrl(found ? found.hook_url : "");
+ } else {
+ setEnabled(false);
+ setIsRecursive(false);
+ setOnEvents(0);
+ assignFromOnEvents(0);
+ setAdditionalHeaders({});
+ setHeaderList([]);
+ setSelectedUrl("");
+ }
+ }, [setting, webhooks]);
+
+ const handleAddHeader = () => {
+ setHeaderList([...headerList, { key: "", value: "" }]);
+ };
+ const handleHeaderChange = (index, field, val) => {
+ const updated = [...headerList];
+ updated[index][field] = val;
+ setHeaderList(updated);
+ };
+ const handleApplyHeaders = () => {
+ const out = {};
+ headerList.forEach(({ key, value }) => {
+ if (key.trim()) out[key] = value;
+ });
+ setAdditionalHeaders(out);
+ };
+
+ const handleCreateWebhook = () => {
+ const newUrl = prompt("Enter the new webhook URL");
+ if (!newUrl) return;
+ createWebhook.mutate(newUrl, {
+ onSuccess: () => alert("Created new Webhook successfully"),
+ onError: () => alert("Failed to create new Webhook"),
+ });
+ };
+ const handleUpdateWebhook = () => {
+ if (!setting || !setting.webhook_id) {
+ alert("No webhook selected. Must pick from dropdown first.");
+ return;
+ }
+ const newUrl = prompt("Enter updated Webhook URL", selectedUrl);
+ if (!newUrl) return;
+ updateWebhook.mutate(
+ { id: setting.webhook_id, data: { hook_url: newUrl } },
+ {
+ onSuccess: () => alert("Updated Webhook successfully"),
+ onError: () => alert("Failed to update Webhook"),
+ }
+ );
+ };
+ const handleDeleteWebhook = () => {
+ if (!setting || !setting.webhook_id) {
+ alert("No webhook selected to delete");
+ return;
+ }
+ if (!window.confirm("Are you sure?")) return;
+ deleteWebhook.mutate(setting.webhook_id, {
+ onSuccess: () => alert("Deleted Webhook successfully"),
+ onError: () => alert("Failed to delete Webhook"),
+ });
+ };
+
+ const handleSaveWebhookSetting = async () => {
+ const hook = webhooks.find((wh) => wh.hook_url === selectedUrl);
+ const payload = {
+ webhook_id: hook? hook.id : null,
+ recursive: isRecursive,
+ additional_header: JSON.stringify(additionalHeaders),
+ enabled,
+ on_events: onEvents,
+
+ };
+ console.log(webhooks);
+ console.log(payload);
+ if(!setting || !setting.id){
+ createWebhookSetting.mutate(payload, {
+ onSuccess: (res) => {
+ updatePathSetting.mutate({id: pathSetting.id, data: {webhook_setting_id: res.id}},{
+ onSuccess: () => alert("Webhook setting successfully created"),
+ onError: () => alert("Failed to save Webhook"),
+ })
+ },
+ onError: () => alert("Failed to save Webhook"),
+ });
+ } else {
+ updateWebhookSetting.mutate({id: setting.id, data: payload}, {
+ onSuccess: () => alert("Updated Webhook successfully"),
+ onError: () => alert("Failed to update Webhook"),
+ });
+ }
+ onClose();
+ };
+
+ return setting ? (
+
+
Webhook Setting
+
+
+
+
+ {isWebhooksLoading ? (
+
Loading...
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+ {setting?.webhook_id && (
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {headerList.map((h, idx) => (
+
+ ))}
+
+
+
+
+
+
+
+ ) : (
+
+ );
+}
+
+
+export default WebhookSettingPanel
\ No newline at end of file
diff --git a/src/utils/markdown-queries.js b/src/utils/markdown-queries.js
index 88f01a1..a66feeb 100644
--- a/src/utils/markdown-queries.js
+++ b/src/utils/markdown-queries.js
@@ -67,9 +67,9 @@ export const useSaveMarkdown = () => {
body: JSON.stringify(data),
})
},{
- onSuccess: (res, variables) => {
- queryClient.invalidateQueries(["markdownsByPath", variables.data.path_id]);
- queryClient.invalidateQueries(["markdown", variables.data.id]);
+ onSuccess: (res) => {
+ queryClient.invalidateQueries(["markdownsByPath", res.path_id]);
+ queryClient.invalidateQueries(["markdown", res.id]);
},
});
};
@@ -93,7 +93,6 @@ export const useMoveMarkdown = () => {
};
export const useSearchMarkdown = (keyword) => {
- const queryClient = useQueryClient();
const config = useConfig();
return useQuery(["markdownsByKeyword", keyword],
() => fetch_(
diff --git a/src/utils/path-queries.js b/src/utils/path-queries.js
index 6e98222..f6f897b 100644
--- a/src/utils/path-queries.js
+++ b/src/utils/path-queries.js
@@ -45,8 +45,8 @@ export const useCreatePath = () => {
body: JSON.stringify(data),
}),
{
- onSuccess: (res, variables) => {
- queryClient.invalidateQueries(["paths", variables.parent_id]);
+ onSuccess: (res) => {
+ queryClient.invalidateQueries(["paths", res.parent_id]);
queryClient.invalidateQueries("tree");
},
}
@@ -63,9 +63,9 @@ export const useUpdatePath = () => {
body: JSON.stringify(data),
}),
{
- onSuccess: (res, variables) => {
+ onSuccess: (res) => {
queryClient.invalidateQueries(["paths", res.parent_id]);
- queryClient.invalidateQueries(["path", variables.data.id]);
+ queryClient.invalidateQueries(["path", res.id]);
queryClient.invalidateQueries("tree");
},
}
diff --git a/src/utils/setting-queries.js b/src/utils/setting-queries.js
new file mode 100644
index 0000000..4f8a56c
--- /dev/null
+++ b/src/utils/setting-queries.js
@@ -0,0 +1,151 @@
+import {fetch_} from "./request-utils";
+import {useConfig} from "../ConfigProvider";
+import {useMutation, useQuery, useQueryClient} from "react-query";
+
+export const usePathSettings = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useQuery(
+ "path_settings",
+ () => fetch_(`${config.BACKEND_HOST}/api/setting/path/`),
+ {
+ onSuccess: (data) => {
+ if(data){
+ for(const setting of data)
+ queryClient.setQueryData(["path_setting", setting.id], setting);
+ }
+ }
+ }
+ );
+};
+
+export const usePathSetting = (setting_id) => {
+ const config = useConfig();
+ return useQuery(
+ ["path_setting", setting_id],
+ () => fetch_(`${config.BACKEND_HOST}/api/setting/path/${setting_id}`),
+ {
+ enabled: !!setting_id,
+ }
+ );
+};
+
+export const useCreatePathSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ (data) => fetch_(`${config.BACKEND_HOST}/api/setting/path/`, {
+ method: "POST",
+ body: JSON.stringify(data)
+ }), {
+ onSuccess: (data) => {
+ queryClient.invalidateQueries(["path_setting", data.id]);
+ queryClient.invalidateQueries("path_setting");
+ }
+ }
+ );
+};
+
+
+export const useUpdatePathSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
+ method: "PATCH",
+ body: JSON.stringify(data)
+ }), {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries(["path_setting", variables.id]);
+ queryClient.invalidateQueries("path_setting");
+ }
+ }
+ );
+};
+
+export const useDeletePathSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ (id) => fetch_(`${config.BACKEND_HOST}/api/setting/path/${id}`, {
+ method: "DELETE",
+ }),{
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries(["path_setting", variables.id]);
+ queryClient.invalidateQueries("path_setting");
+ }
+ }
+ );
+};
+
+export const useMarkdownSettings = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useQuery(
+ "markdown_setting",
+ () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`),
+ {
+ onSuccess: (data) => {
+ if(data){
+ for(const setting of data)
+ queryClient.invalidateQueries(["markdown_setting", setting.id]);
+ }
+ }
+ }
+ );
+};
+
+export const useMarkdownSetting = (setting_id) => {
+ const config = useConfig();
+ return useQuery(
+ ["markdown_setting", setting_id],
+ () => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`), {
+ enabled: !!setting_id,
+ }
+ );
+};
+
+
+export const useCreateMarkdownSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ (data) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`, {
+ method: "POST",
+ body: JSON.stringify(data)
+ }), {
+ onSuccess: (data) => {
+ queryClient.invalidateQueries(["path_setting", data.id]);
+ }
+ }
+ );
+};
+
+
+export const useUpdateMarkdownSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ ({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
+ method: "PATCH",
+ body: JSON.stringify(data)
+ }),{
+ onSuccess: (data) => {
+ queryClient.invalidateQueries(["path_setting", data.id]);
+ }
+ });
+};
+
+export const useDeleteMarkdownSetting = () => {
+ const config = useConfig();
+ const queryClient = useQueryClient();
+ return useMutation(
+ (id) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${id}`, {
+ method: "DELETE",
+ }),{
+ onSuccess: (data) => {
+ queryClient.invalidateQueries(["path_setting", data.id]);
+ }
+ }
+ );
+};
diff --git a/src/utils/webhook-queries.js b/src/utils/webhook-queries.js
index 1959661..28eebb8 100644
--- a/src/utils/webhook-queries.js
+++ b/src/utils/webhook-queries.js
@@ -17,7 +17,7 @@ export const useWebhooks = () =>{
);
};
-export const useCreateWebhook = () =>{
+export const useCreateWebhook = () => {
const config = useConfig();
const queryClient = useQueryClient();
@@ -78,7 +78,6 @@ export const useWebhookSettings = () => {
if(data){
for(const setting of data){
queryClient.setQueryData(["webhook_setting", setting.id], setting);
- queryClient.setQueryData(["webhook_setting_path_id", setting.path_id], setting);
}
}
}
@@ -88,33 +87,14 @@ export const useWebhookSettings = () => {
export const useWebhookSetting = (setting_id) => {
const config = useConfig();
- const queryClient = useQueryClient();
return useQuery(
["webhook_setting", setting_id],
() => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/${setting_id}`),
{
enabled: !!setting_id,
- onSuccess: (res) => {
- if(res)
- queryClient.setQueryData(["webhook_setting_path_id", res.path_id], res);
- }
});
};
-export const useWebhookSettingByPathId = (pathId) => {
- const config = useConfig();
- const queryClient = useQueryClient();
- return useQuery(
- ["webhook_setting_path_id", pathId],
- () => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/path/${pathId}`),
- {
- enabled: !!pathId,
- onSuccess: (res) => {
- if(res)
- queryClient.setQueryData(["webhook_setting", res.id], res);
- }
- });
-};
export const useCreateWebhookSetting = () => {
const config = useConfig();
@@ -126,7 +106,6 @@ export const useCreateWebhookSetting = () => {
}),{
onSuccess: (res) => {
queryClient.invalidateQueries(["webhook_setting", res.id]);
- queryClient.invalidateQueries(["webhook_setting_path_id", res.path_id]);
queryClient.invalidateQueries("webhook_setting");
}
}
@@ -143,7 +122,6 @@ export const useUpdateWebhookSetting = () => {
}),{
onSuccess: (res, variables) => {
queryClient.invalidateQueries(["webhook_setting", variables.id]);
- queryClient.invalidateQueries(["webhook_setting_path_id", variables.path_id]);
queryClient.invalidateQueries("webhook_setting");
}
}
@@ -161,7 +139,6 @@ export const useDeleteWebhookSetting = () => {
{
onSuccess: (res, variables) => {
queryClient.invalidateQueries(["webhook_setting", variables.id]);
- queryClient.invalidateQueries(["webhook_setting_path_id", variables.path_id]);
queryClient.invalidateQueries("webhook_setting");
}
}