improve: change db schema for settings
This commit is contained in:
@@ -1,363 +1,71 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
useCreateWebhookSetting,
|
||||
useUpdateWebhookSetting,
|
||||
useWebhookSettingByPathId,
|
||||
useWebhooks,
|
||||
useCreateWebhook,
|
||||
useUpdateWebhook,
|
||||
useDeleteWebhook
|
||||
} from "../../utils/webhook-queries";
|
||||
import {useUpdatePath} from "../../utils/path-queries";
|
||||
import {useCreatePathSetting, usePathSetting} from "../../utils/setting-queries";
|
||||
import WebhookSettingPanel from "../Settings/PathSettings/WebhookSettingPanel";
|
||||
import React, {useState} from "react";
|
||||
const PathSettingModal = ({ isOpen, path, onClose }) => {
|
||||
const {data: pathSetting, isLoading: isPathSettingLoading} = usePathSetting(path?.setting_id || 0);
|
||||
const createPathSetting = useCreatePathSetting();
|
||||
const updatePath = useUpdatePath();
|
||||
|
||||
const PathSettingModal = ({ pathId, isOpen, onClose }) => {
|
||||
const { data: setting } = useWebhookSettingByPathId(pathId);
|
||||
const { data: webhooks } = useWebhooks();
|
||||
|
||||
const createWebhookSetting = useCreateWebhookSetting();
|
||||
const updateWebhookSetting = useUpdateWebhookSetting();
|
||||
const createWebhook = useCreateWebhook();
|
||||
const updateWebhook = useUpdateWebhook();
|
||||
const deleteWebhook = useDeleteWebhook();
|
||||
|
||||
const [url, setUrl] = useState("");
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const [webhookId, setWebhookId] = useState(-1);
|
||||
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 [triggerEvents, setTriggerEvents] = useState(0);
|
||||
const [isRecursive, setIsRecursive] = useState(false);
|
||||
const [additionalHeaders, setAdditionalHeaders] = useState({});
|
||||
const [headerList, setHeaderList] = useState([]);
|
||||
const handleTriggerEventsUpdate = (eventType, isChecked) => {
|
||||
setTriggerEvents((prevEvents) => {
|
||||
let newEvents = prevEvents;
|
||||
|
||||
switch (eventType) {
|
||||
case "isOnMarkdownCreated":
|
||||
newEvents = isChecked ? (newEvents | 1) : (newEvents & ~1);
|
||||
break;
|
||||
case "isOnMarkdownUpdated":
|
||||
newEvents = isChecked ? (newEvents | 2) : (newEvents & ~2);
|
||||
break;
|
||||
case "isOnMarkdownDeleted":
|
||||
newEvents = isChecked ? (newEvents | 4) : (newEvents & ~4);
|
||||
break;
|
||||
case "isOnPathCreated":
|
||||
newEvents = isChecked ? (newEvents | 8) : (newEvents & ~8);
|
||||
break;
|
||||
case "isOnPathUpdated":
|
||||
newEvents = isChecked ? (newEvents | 16) : (newEvents & ~16);
|
||||
break;
|
||||
case "isOnPathDeleted":
|
||||
newEvents = isChecked ? (newEvents | 32) : (newEvents & ~32);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
const [activeTab, setActiveTab] = useState("webhook");
|
||||
const handleCreatePathSetting = () => {
|
||||
createPathSetting.mutate({}, {
|
||||
onSuccess: (res) => {
|
||||
updatePath.mutate({id: path.id, data: {setting_id: res.id}});
|
||||
}
|
||||
|
||||
return newEvents;
|
||||
});
|
||||
};
|
||||
|
||||
const assignFromTriggerEvents = (events) => {
|
||||
setIsOnMarkdownCreated((events & 1) > 0);
|
||||
setIsOnMarkdownUpdated((events & 2) > 0);
|
||||
setIsOnMarkdownDeleted((events & 4) > 0);
|
||||
setIsOnPathCreated((events & 8) > 0);
|
||||
setIsOnPathUpdated((events & 16) > 0);
|
||||
setIsOnPathDeleted((events & 32) > 0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (setting && webhooks) {
|
||||
setEnabled(setting.enabled);
|
||||
setWebhookId(setting.webhook_id || -1);
|
||||
const selectedWebhook = webhooks?.find(hook => hook.id === setting.webhook_id);
|
||||
if (selectedWebhook) {
|
||||
setUrl(selectedWebhook.hook_url);
|
||||
}
|
||||
setTriggerEvents(setting.on_events);
|
||||
assignFromTriggerEvents(setting.on_events);
|
||||
setIsRecursive(setting.recursive);
|
||||
try{
|
||||
const headers = setting.additional_header ?
|
||||
JSON.parse(setting.additional_header) : {};
|
||||
setAdditionalHeaders(headers);
|
||||
setHeaderList(Object.entries(headers).map(([key, value]) => ({key, value })));
|
||||
} catch(err) {
|
||||
setAdditionalHeaders({});
|
||||
setHeaderList([]);
|
||||
}
|
||||
} else {
|
||||
setUrl("");
|
||||
setEnabled(false);
|
||||
setWebhookId(-1);
|
||||
setTriggerEvents(0);
|
||||
assignFromTriggerEvents(0);
|
||||
setIsRecursive(false);
|
||||
setAdditionalHeaders({});
|
||||
setHeaderList([]);
|
||||
}
|
||||
}, [setting, webhooks]);
|
||||
|
||||
const handleSave = () => {
|
||||
const payload = {
|
||||
path_id: pathId,
|
||||
webhook_id: webhookId,
|
||||
enabled,
|
||||
on_events: triggerEvents,
|
||||
recursive: isRecursive,
|
||||
additional_header: JSON.stringify(additionalHeaders),
|
||||
};
|
||||
console.log(payload);
|
||||
if (!setting) {
|
||||
createWebhookSetting.mutate(payload, {
|
||||
onSuccess: () => alert("Webhook setting created successfully"),
|
||||
onError: () => alert("Webhook setting creation failed"),
|
||||
});
|
||||
} else {
|
||||
updateWebhookSetting.mutate({ id: setting.id, data: payload }, {
|
||||
onSuccess: () => alert("Webhook setting updated successfully"),
|
||||
onError: () => alert("Webhook setting update failed"),
|
||||
});
|
||||
}
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleCreateWebhook = () => {
|
||||
const newUrl = prompt("Enter new webhook URL");
|
||||
if (newUrl) {
|
||||
createWebhook.mutate(newUrl, {
|
||||
onSuccess: () => alert("Webhook created successfully"),
|
||||
onError: () => alert("Webhook creation failed"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateWebhook = () => {
|
||||
const newUrl = prompt("Enter new webhook URL", url);
|
||||
if (newUrl && webhookId !== -1) {
|
||||
updateWebhook.mutate({ id: webhookId, data: { hook_url: newUrl } }, {
|
||||
onSuccess: () => alert("Webhook updated successfully"),
|
||||
onError: () => alert("Webhook update failed"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteWebhook = () => {
|
||||
if (webhookId !== -1 && window.confirm("Are you sure you want to delete this webhook?")) {
|
||||
deleteWebhook.mutate(webhookId, {
|
||||
onSuccess: () => alert("Webhook deleted successfully"),
|
||||
onError: () => alert("Webhook deletion failed"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleApplyHeaders = () => {
|
||||
const newHeaders = {};
|
||||
headerList.forEach(({ key, value }) => {
|
||||
if (key.trim()) newHeaders[key] = value;
|
||||
});
|
||||
setAdditionalHeaders(newHeaders);
|
||||
};
|
||||
|
||||
const handleHeaderChange = (index, field, value) => {
|
||||
const updatedHeaders = [...headerList];
|
||||
updatedHeaders[index][field] = value;
|
||||
setHeaderList(updatedHeaders);
|
||||
};
|
||||
const handleAddHeader = () => {
|
||||
setHeaderList([...headerList, { key: "", value: "" }])
|
||||
};
|
||||
if(isPathSettingLoading)
|
||||
return <p>Loading...</p>
|
||||
|
||||
return (
|
||||
<div className={`modal ${isOpen ? "is-active" : ""}`}>
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-card">
|
||||
<div className="modal-background" onClick={onClose}></div>
|
||||
<div className="modal-card" style={{width: "60vw"}}>
|
||||
<header className="modal-card-head">
|
||||
<p className="modal-card-title">Webhook Settings</p>
|
||||
<button className="delete" aria-label="close" onClick={onClose}></button>
|
||||
<p className="modal-card-title">Path Settings</p>
|
||||
<button type="button" className="delete" aria-label="close" onClick={onClose} />
|
||||
</header>
|
||||
<section className="modal-card-body">
|
||||
<form>
|
||||
<div className="field">
|
||||
<label className="label">Webhook URL</label>
|
||||
<div className="field has-addons">
|
||||
<div className="control is-expanded">
|
||||
<div className="select is-fullwidth">
|
||||
<select
|
||||
value={url}
|
||||
onChange={(e) => {
|
||||
setUrl(e.target.value);
|
||||
const selectedWebhook = webhooks?.find(h => h.hook_url === e.target.value);
|
||||
setWebhookId(selectedWebhook?.id || -1);
|
||||
}}
|
||||
>
|
||||
<option value="">Select a webhook</option>
|
||||
{webhooks?.map(hook => (
|
||||
<option key={hook.id} value={hook.hook_url}>{hook.hook_url}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="control">
|
||||
<button type="button" className="button is-primary" onClick={handleCreateWebhook}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
{webhookId !== -1 && (
|
||||
<div className="buttons">
|
||||
<button type="button" className="button is-info" onClick={handleUpdateWebhook}>Update Webhook URL</button>
|
||||
<button type="button" className="button is-danger" onClick={handleDeleteWebhook}>Delete Webhook</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="field">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
onChange={(e) => setEnabled(e.target.checked)}
|
||||
/>
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">On Events</label>
|
||||
<div className="box">
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownCreated}
|
||||
onChange={(e) => {
|
||||
setIsOnMarkdownCreated(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnMarkdownCreated", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Markdown Created
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownUpdated}
|
||||
onChange={(e) => {
|
||||
setIsOnMarkdownUpdated(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnMarkdownUpdated", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Markdown Updated
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownDeleted}
|
||||
onChange={(e) => {
|
||||
setIsOnMarkdownDeleted(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnMarkdownDeleted", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Markdown Deleted
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="column">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathCreated}
|
||||
onChange={(e) => {
|
||||
setIsOnPathCreated(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnPathCreated", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Path Created
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathUpdated}
|
||||
onChange={(e) => {
|
||||
setIsOnPathUpdated(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnPathUpdated", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Path Updated
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathDeleted}
|
||||
onChange={(e) => {
|
||||
setIsOnPathDeleted(e.target.checked);
|
||||
handleTriggerEventsUpdate("isOnPathDeleted", e.target.checked);
|
||||
}}
|
||||
/>
|
||||
Path Deleted
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isRecursive}
|
||||
onChange={(e) => setIsRecursive(e.target.checked)}
|
||||
/>
|
||||
Recursive
|
||||
</label>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="checkbox">Additional Headers</label>
|
||||
<div className="box">
|
||||
{headerList.map((header, index) => (
|
||||
<div className="columns" key={index}>
|
||||
<div className="column">
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="key"
|
||||
value={header.key}
|
||||
onChange={(e) => {handleHeaderChange(index, "key", e.target.value)}}
|
||||
/>
|
||||
</div>
|
||||
<div className="column">
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="value"
|
||||
value={header.value}
|
||||
onChange={(e) => handleHeaderChange(index, "value", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button type="button" className="button is-small is-info" onClick={handleAddHeader}>Add Header</button>
|
||||
<button type="button" className="button is-small is-success" onClick={handleApplyHeaders}>Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
{!pathSetting && !isPathSettingLoading ? (
|
||||
<section className="modal-card-body">
|
||||
<button
|
||||
type="button"
|
||||
className="button is-primary"
|
||||
onClick={handleCreatePathSetting}
|
||||
>
|
||||
Create Path Setting
|
||||
</button>
|
||||
</section>
|
||||
) : (
|
||||
<section className="modal-card-body">
|
||||
<div className="tabs">
|
||||
<ul>
|
||||
<li className={activeTab === "webhook" ? "is-active" : ""}>
|
||||
<a onClick={() => setActiveTab("webhook")}>Webhook</a>
|
||||
</li>
|
||||
<li className={activeTab === "template" ? "is-active" : ""}>
|
||||
<a onClick={() => setActiveTab("template")}>Template</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<footer className="modal-card-foot">
|
||||
<button className="button is-success" type="button" onClick={handleSave}>Save</button>
|
||||
<button className="button" onClick={onClose}>Cancel</button>
|
||||
</footer>
|
||||
{activeTab === "webhook" && (
|
||||
<WebhookSettingPanel
|
||||
pathSetting={pathSetting}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)}
|
||||
{activeTab === "template" && (
|
||||
<div></div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default PathSettingModal;
|
||||
@@ -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 }) => {
|
||||
<div className="path-manager">
|
||||
<div className="path-manager-header field has-addons">
|
||||
<div className="current-path control">
|
||||
{currentPath.map((path, index) => (
|
||||
{currentFullPath.map((path, index) => (
|
||||
<span
|
||||
key={path.id}
|
||||
className="breadcrumb-item is-clickable"
|
||||
onClick={() => handlePathClick(path.id, index)}
|
||||
>
|
||||
{path.name}
|
||||
{index < currentPath.length - 1 && " / "}
|
||||
{index < currentFullPath.length - 1 && " / "}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -137,7 +138,7 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
||||
</button>
|
||||
<PathSettingModal
|
||||
isOpen={isPathSettingModalOpen}
|
||||
pathId={currentPathId}
|
||||
path={currentPath}
|
||||
onClose={() => setIsPathSettingModalModalOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
433
src/components/Settings/PathSettings/WebhookSettingPanel.js
Normal file
433
src/components/Settings/PathSettings/WebhookSettingPanel.js
Normal file
@@ -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 ? (
|
||||
<div className="box" style={{ marginTop: "1rem" }}>
|
||||
<h4 className="title is-5">Webhook Setting</h4>
|
||||
<div className="field">
|
||||
<label className="label">Select or Create a Webhook</label>
|
||||
<div className="field has-addons">
|
||||
<div className="control is-expanded">
|
||||
{isWebhooksLoading ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<div className="select is-fullwidth">
|
||||
<select
|
||||
value={selectedUrl}
|
||||
onChange={(e) => setSelectedUrl(e.target.value)}
|
||||
>
|
||||
<option value="">(none)</option>
|
||||
{webhooks.map((hook) => (
|
||||
<option key={hook.id} value={hook.hook_url}>
|
||||
{hook.hook_url}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="control">
|
||||
<button
|
||||
type="button"
|
||||
className="button is-primary"
|
||||
onClick={handleCreateWebhook}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{setting?.webhook_id && (
|
||||
<div className="buttons" style={{ marginTop: "0.5rem" }}>
|
||||
<button
|
||||
type="button"
|
||||
className="button is-info"
|
||||
onClick={handleUpdateWebhook}
|
||||
>
|
||||
Update Webhook URL
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="button is-danger"
|
||||
onClick={handleDeleteWebhook}
|
||||
>
|
||||
Delete Webhook
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
onChange={(e) => setEnabled(e.target.checked)}
|
||||
/>
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">On Events</label>
|
||||
<div className="box">
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownCreated}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("MARKDOWN_CREATED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Markdown Created
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownUpdated}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("MARKDOWN_UPDATED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Markdown Updated
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnMarkdownDeleted}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("MARKDOWN_DELETED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Markdown Deleted
|
||||
</label>
|
||||
</div>
|
||||
<div className="column">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathCreated}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("PATH_CREATED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Path Created
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathUpdated}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("PATH_UPDATED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Path Updated
|
||||
</label>
|
||||
<br />
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isOnPathDeleted}
|
||||
onChange={(e) =>
|
||||
handleTriggerEventsUpdate("PATH_DELETED", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Path Deleted
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isRecursive}
|
||||
onChange={(e) => setIsRecursive(e.target.checked)}
|
||||
/>
|
||||
Recursive
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Additional Headers</label>
|
||||
<div className="box">
|
||||
{headerList.map((h, idx) => (
|
||||
<div className="columns" key={idx}>
|
||||
<div className="column">
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="key"
|
||||
value={h.key}
|
||||
onChange={(e) => handleHeaderChange(idx, "key", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="column">
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="value"
|
||||
value={h.value}
|
||||
onChange={(e) =>
|
||||
handleHeaderChange(idx, "value", e.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="button is-small is-info"
|
||||
onClick={handleAddHeader}
|
||||
>
|
||||
+ Header
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="button is-small is-success"
|
||||
onClick={handleApplyHeaders}
|
||||
style={{ marginLeft: "0.5rem" }}
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="button is-primary"
|
||||
onClick={handleSaveWebhookSetting}
|
||||
>
|
||||
Save Webhook Setting
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="button is-primary"
|
||||
type="button"
|
||||
onClick={handleCreateWebhookSetting}
|
||||
>
|
||||
Create Webhook Setting
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default WebhookSettingPanel
|
||||
@@ -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_(
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
}
|
||||
|
||||
151
src/utils/setting-queries.js
Normal file
151
src/utils/setting-queries.js
Normal file
@@ -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]);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user