{
}}
labelId="suborg-changer"
value={workflow.org_id}
- disabled={savingState !== 0 || suborgWorkflows?.length === 0}
+ disabled={savingState !== 0 || suborgWorkflows?.length === 0 || allTriggers === undefined}
onChange={(e) => {
if (lastSaved === false && originalWorkflow.id === workflow.id) {
setSuborgWorkflows([])
saveWorkflow(workflow, undefined, undefined, e.target.value)
- toast.warn("Saving workflow first due to detected changes. Please try to change workflow again when it is finished.", {
+ toast.warn(`Saving workflow first due to detected changes. If more than 10 auth`, {
autoClose: 2000,
})
return
@@ -17693,6 +17761,7 @@ const AngularWorkflow = (defaultprops) => {
}
ReactDOM.unstable_batchedUpdates(() => {
+ setAllTriggers(undefined)
setSelectedTriggerIndex(-1)
getEnvironments(e.target.value)
getAppAuthentication(undefined, undefined, undefined, e.target.value)
diff --git a/frontend/src/views/ApiExplorerWrapper.jsx b/frontend/src/views/ApiExplorerWrapper.jsx
index 72dc93ca..96298437 100644
--- a/frontend/src/views/ApiExplorerWrapper.jsx
+++ b/frontend/src/views/ApiExplorerWrapper.jsx
@@ -663,7 +663,7 @@ const ApiExplorerWrapper = (props) => {
}
}
} else if (validate.result.status === 404) {
- toast.error("Page not found. Please try a different URL.")
+ //toast.error("Page not found. Please try a different URL.")
} else if (validate.result.error !== undefined && validate.result.error !== null && validate.result.error.length > 0) {
if (validate.result.error.toLowerCase().includes("max retries")) {
toast.error("Are you sure the URL is correct? It seems like the server is not responding.")
diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx
index af2cc2ad..2d1bbb32 100755
--- a/frontend/src/views/AppCreator.jsx
+++ b/frontend/src/views/AppCreator.jsx
@@ -281,7 +281,7 @@ export const appCategories = [
"name": "AI",
"color": "#FFC107",
"icon": "AI",
- "action_labels": ["Answer Question", "Run Action"],
+ "action_labels": ["Answer Question", "Run Action", "Run LLM",],
},
{
"name": "Other",
diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx
index 7d55acaf..836ada35 100755
--- a/frontend/src/views/Docs.jsx
+++ b/frontend/src/views/Docs.jsx
@@ -1215,7 +1215,7 @@ const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
minWidth: isMobile ? null : (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
position: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? "relative" : "static",
left: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? 120 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0,
- marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && (isLoggedIn && isLoaded) ? 160 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
+ marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && (isLoggedIn && isLoaded) ? 90 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
transition: "left 0.3s ease-in-out, min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, position 0.3s ease-in-out, margin 0.3s ease-in-out, margin-left 0.3s ease"
}}>
{children}
diff --git a/frontend/src/views/Usecases2.jsx b/frontend/src/views/Usecases2.jsx
index 9648881e..886427b3 100644
--- a/frontend/src/views/Usecases2.jsx
+++ b/frontend/src/views/Usecases2.jsx
@@ -23,6 +23,7 @@ import {
Chip,
Checkbox,
Fade,
+ Skeleton,
} from "@mui/material";
import {
@@ -140,12 +141,166 @@ const UsecaseListComponent = (props) => {
const [firstLoad, setFirstLoad] = useState(true)
const [apps, setApps] = useState([])
+ const [autoOpenUsecase, setAutoOpenUsecase] = useState(null);
const classes = useStyles();
let navigate = useNavigate();
const [mitreTags, setMitreTags] = useState([]);
+ // Add loading state
+ const [isLoading, setIsLoading] = useState(true);
+
+ // Add this useEffect to handle URL parameters on load
+ useEffect(() => {
+ const urlSearchParams = new URLSearchParams(window.location.search);
+ const params = Object.fromEntries(urlSearchParams.entries());
+
+ const selectedUsecase = params["selected_object"];
+ if (selectedUsecase && keys.length > 0) {
+ const usecaseName = selectedUsecase.toLowerCase().replaceAll("_", " ");
+
+ // Find the matching usecase in the keys
+ for (const category of keys) {
+ const foundUsecase = category.list.find(
+ usecase => usecase.name.toLowerCase().replaceAll("_", " ") === usecaseName
+ );
+
+ if (foundUsecase) {
+ setAutoOpenUsecase(foundUsecase);
+
+ // Wait for render then scroll
+ setTimeout(() => {
+ const element = document.getElementById(usecaseName);
+ if (element) {
+ element.scrollIntoView({
+ behavior: "smooth",
+ block: "center",
+ inline: "center"
+ });
+ }
+ }, 1000);
+
+ break;
+ }
+ }
+ }
+ }, [keys]);
+
+ // Add useEffect to handle auto-opening
+ useEffect(() => {
+ if (autoOpenUsecase) {
+ getUsecase(autoOpenUsecase, 0, 0);
+ setAutoOpenUsecase(null);
+ }
+ }, [autoOpenUsecase]);
+
+ // Loading skeleton component
+ const LoadingSkeleton = () => (
+
+ {/* Header skeleton */}
+
+
+
+ {/* Apps selection skeleton */}
+
+
+ {/* App icons skeleton */}
+
+ {[1, 2, 3, 4, 5].map((app) => (
+
+ ))}
+
+ {/* Add more apps button skeleton */}
+
+
+
+ {/* Usecase categories skeleton - 3 sections: Collect, Enrich, Detect */}
+ {[1, 2, 3,4,5].map((category) => (
+
+ {/* Category title with color indicator */}
+
+
+
+
+ {[1, 2, 3].map((item) => (
+
+
+ {/* App icons container */}
+
+
+
+
+ {/* Usecase title */}
+
+
+
+ ))}
+
+
+ ))}
+
+ );
+
+
const parseUsecase = (subcase, inputFramework) => {
var useFramework = frameworkData
@@ -187,6 +342,7 @@ const UsecaseListComponent = (props) => {
}, [frameworkData])
const loadApps = () => {
+ setIsLoading(true);
fetch(`${globalUrl}/api/v1/apps`, {
method: "GET",
headers: {
@@ -220,9 +376,11 @@ const UsecaseListComponent = (props) => {
}
setApps(responseJson);
+ setIsLoading(false);
})
.catch((error) => {
console.log("App loading error: " + error.toString());
+ setIsLoading(false);
})
}
@@ -231,15 +389,23 @@ const UsecaseListComponent = (props) => {
}, [])
if (keys === undefined || keys === null || keys.length === 0) {
- return null
+ return ;
}
// Timeout 50ms to delay it slightly
const getUsecase = (subcase, index, subindex) => {
- subcase = parseUsecase(subcase)
- setPrevSubcase(subcase)
+ // Update URL with selected usecase
+ const usecaseName = subcase.name.toLowerCase().replaceAll(" ", "_")
+ const newUrl = `?selected_object=${usecaseName}`
+
+ // Force URL update even if it's the same usecase
+ navigate(newUrl, { replace: true })
+
+ // Parse and fetch usecase data
+ subcase = parseUsecase(subcase)
+ setPrevSubcase(subcase)
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
method: "GET",
@@ -415,6 +581,19 @@ const UsecaseListComponent = (props) => {
}
}
+ // Then, add a cleanup function for URL params when drawer closes
+ const handleUsecaseClose = () => {
+ // Remove the selected_object parameter from URL
+ navigate("/usecases", { replace: true })
+
+ // Reset relevant state
+ setInputUsecase({})
+ setExpandedIndex(-1)
+ setExpandedItem(-1)
+ setFirstLoad(false)
+ setSelectedWorkflows([])
+ }
+
return (
@@ -645,10 +824,10 @@ const UsecaseListComponent = (props) => {
return (
{
- if (fixedName === "reporting") {
- getUsecase(subcase, index, subindex)
- return
- }
+ // if (fixedName === "reporting") {
+ // getUsecase(subcase, index, subindex)
+ // return
+ // }
//setSelectedWorkflows([])
if (selectedItem) {
@@ -678,6 +857,8 @@ const UsecaseListComponent = (props) => {
workflowBuilt={workflowBuilt}
inputWorkflowId={workflowBuilt}
usecaseDetails={usecaseDetails}
+ isModalOpenDefault={autoOpenUsecase?.name === subcase.name}
+ onClose={handleUsecaseClose}
/>
@@ -717,6 +898,7 @@ const Usecases2 = (props) => {
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
+ const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (selectedUsecaseCategory.length === 0) {
@@ -729,71 +911,8 @@ const Usecases2 = (props) => {
}
}, [selectedUsecaseCategory])
- const checkSelectedParams = () => {
- const urlSearchParams = new URLSearchParams(window.location.search)
- const params = Object.fromEntries(urlSearchParams.entries())
-
- const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname;
- const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
-
- const foundQuery = params["selected"]
- if (foundQuery !== null && foundQuery !== undefined) {
- setSelectedUsecaseCategory(foundQuery)
-
- const newitem = removeParam("selected", cursearch);
- navigate(curpath + newitem)
- }
-
- const baseItem = document.getElementById("reporting")
- if (baseItem !== undefined && baseItem !== null) {
- baseItem.click()
-
- // Find close window button -> go to top
- const foundButton = document.getElementById("close_selection")
- if (foundButton !== undefined && foundButton !== null) {
- foundButton.click()
- }
-
- // Scroll back to top
- window.scrollTo(0, 0)
- }
-
- const foundQuery2 = params["selected_object"]
- if (foundQuery2 !== null && foundQuery2 !== undefined) {
- // Take a random object, quickly click it, then go to this one
- // Something is weird with loading apps without it
-
- const queryName = foundQuery2.toLowerCase().replaceAll("_", " ")
- // Waiting a bit for it to render
- setTimeout(() => {
- const foundItem = document.getElementById(queryName)
- if (foundItem !== undefined && foundItem !== null) {
- foundItem.click()
- // Scroll to it
-
- setTimeout(() => {
- foundItem.scrollIntoView({
- behavior: "smooth",
- block: "center",
- inline: "center"
- })
- }, 100)
- } else {
- //console.log("Couldn't find item with name ", queryName)
- }
- }, 1000)
- }
-
- }
-
- useEffect(() => {
- if (usecases.length > 0) {
- //console.log(usecases)
- checkSelectedParams()
- }
- }, [usecases])
-
const getFramework = () => {
+ setIsLoading(true);
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
method: "GET",
headers: {
@@ -831,6 +950,7 @@ const Usecases2 = (props) => {
})
.catch((error) => {
toast(error.toString());
+ setIsLoading(false);
})
}
diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod
index a883723c..9369e1dc 100644
--- a/functions/onprem/orborus/go.mod
+++ b/functions/onprem/orborus/go.mod
@@ -10,7 +10,7 @@ require (
github.com/docker/docker v27.5.0+incompatible
github.com/docker/go-connections v0.5.0
github.com/satori/go.uuid v1.2.0
- github.com/shuffle/shuffle-shared v0.7.92
+ github.com/shuffle/shuffle-shared v0.7.95
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
)
diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod
index ea1077ba..209dd0db 100644
--- a/functions/onprem/worker/go.mod
+++ b/functions/onprem/worker/go.mod
@@ -8,7 +8,7 @@ require (
github.com/docker/docker v27.5.0+incompatible
github.com/gorilla/mux v1.8.1
github.com/satori/go.uuid v1.2.0
- github.com/shuffle/shuffle-shared v0.7.92
+ github.com/shuffle/shuffle-shared v0.7.95
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
k8s.io/client-go v0.30.2