Merge branch 'main' into nightly
This commit is contained in:
@@ -20,19 +20,19 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- app: frontend
|
- app: frontend
|
||||||
path: frontend
|
path: frontend
|
||||||
version: 2.1.2
|
version: 2.1.3
|
||||||
experimental: true
|
experimental: true
|
||||||
- app: backend
|
- app: backend
|
||||||
path: backend
|
path: backend
|
||||||
version: 2.1.2
|
version: 2.1.3
|
||||||
experimental: true
|
experimental: true
|
||||||
- app: orborus
|
- app: orborus
|
||||||
path: functions/onprem/orborus
|
path: functions/onprem/orborus
|
||||||
version: 2.1.2
|
version: 2.1.3
|
||||||
experimental: true
|
experimental: true
|
||||||
- app: worker
|
- app: worker
|
||||||
path: functions/onprem/worker
|
path: functions/onprem/worker
|
||||||
version: 2.1.2
|
version: 2.1.3
|
||||||
experimental: true
|
experimental: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -1129,7 +1129,7 @@ const AppFramework = (props) => {
|
|||||||
}, [newSelectedApp])
|
}, [newSelectedApp])
|
||||||
|
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
const imgSize = 50;
|
const imgSize = 50;
|
||||||
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
|
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ const AppSelection = props => {
|
|||||||
document.title = "Choose your apps"
|
document.title = "Choose your apps"
|
||||||
const ref = useRef()
|
const ref = useRef()
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||||
|
|||||||
+104
-149
@@ -43,6 +43,8 @@ const ChatBot = (props) => {
|
|||||||
const [appname, setAppname] = useState("");
|
const [appname, setAppname] = useState("");
|
||||||
const [threadId, setThreadId] = useState("");
|
const [threadId, setThreadId] = useState("");
|
||||||
const [runId, setRunId] = useState("");
|
const [runId, setRunId] = useState("");
|
||||||
|
const [responseId, setResponseId] = useState("");
|
||||||
|
const [conversationId, setConversationId] = useState("");
|
||||||
|
|
||||||
// New state for thread management
|
// New state for thread management
|
||||||
const [isLoadingThread, setIsLoadingThread] = useState(false);
|
const [isLoadingThread, setIsLoadingThread] = useState(false);
|
||||||
@@ -54,7 +56,7 @@ const ChatBot = (props) => {
|
|||||||
const [showAppSearch, setShowAppSearch] = useState(false);
|
const [showAppSearch, setShowAppSearch] = useState(false);
|
||||||
|
|
||||||
// Get thread ID from URL params
|
// Get thread ID from URL params
|
||||||
const { threadId: urlThreadId } = useParams();
|
const { conversationId: urlConversationId } = useParams();
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
const waitingMsg = "Processing..."
|
const waitingMsg = "Processing..."
|
||||||
@@ -93,24 +95,24 @@ const ChatBot = (props) => {
|
|||||||
}
|
}
|
||||||
}, [appname])
|
}, [appname])
|
||||||
|
|
||||||
// Load existing thread if threadId is in URL
|
// Load existing conversation if conversationId is in URL
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (urlThreadId && urlThreadId !== threadId) {
|
if (urlConversationId && urlConversationId !== conversationId) {
|
||||||
loadExistingThread(urlThreadId);
|
loadExistingThread(urlConversationId);
|
||||||
}
|
}
|
||||||
}, [urlThreadId]);
|
}, [urlConversationId]);
|
||||||
|
|
||||||
const loadExistingThread = (threadIdToLoad) => {
|
const loadExistingThread = (conversationIdToLoad) => {
|
||||||
setIsLoadingThread(true);
|
setIsLoadingThread(true);
|
||||||
setThreadError("");
|
setThreadError("");
|
||||||
|
|
||||||
fetch(`${globalUrl}/api/v1/conversation/thread`, {
|
fetch(`${globalUrl}/api/v1/conversation/history`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
body: JSON.stringify({ thread_id: threadIdToLoad }),
|
body: JSON.stringify({ conversation_id: conversationIdToLoad }),
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
@@ -138,22 +140,22 @@ const ChatBot = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set thread data - this is crucial for continuing the conversation
|
// Set conversation data - this is crucial for continuing the conversation
|
||||||
setThreadId(data.thread_id);
|
setConversationId(data.conversation_id);
|
||||||
console.log("Loaded existing thread:", data.thread_id);
|
|
||||||
|
|
||||||
// Transform API message format to UI format
|
// Transform API message format to UI format
|
||||||
const transformedMessages = (data.messages || []).map((msg, index) => ({
|
const transformedMessages = (data.messages || []).map((msg, index) => ({
|
||||||
id: `${data.thread_id}_${index}`,
|
id: `${data.conversation_id}_${index}`,
|
||||||
status: msg.role === "user" ? "sent" : "received",
|
status: msg.role === "user" ? "sent" : "received",
|
||||||
message: msg.content,
|
message: msg.content,
|
||||||
timestamp: msg.timestamp
|
timestamp: msg.timestamp
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setMessages(transformedMessages);
|
setMessages(transformedMessages);
|
||||||
setThreadOrgId(data.thread_org_id);
|
setThreadOrgId(data.org_id);
|
||||||
setIsActiveOrg(data.is_active_org);
|
setIsActiveOrg(data.is_active_org);
|
||||||
|
|
||||||
|
// Disable chat if not in active org
|
||||||
if (!data.is_active_org) {
|
if (!data.is_active_org) {
|
||||||
setChatDisabled(true);
|
setChatDisabled(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -260,12 +262,9 @@ const ChatBot = (props) => {
|
|||||||
const sentId = uuidv4();
|
const sentId = uuidv4();
|
||||||
var parsedData = {
|
var parsedData = {
|
||||||
"query": inputmsg,
|
"query": inputmsg,
|
||||||
"thread_id": threadId,
|
"conversation_id": conversationId,
|
||||||
"run_id": runId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Sending message with thread_id:", threadId);
|
|
||||||
|
|
||||||
if (appname !== undefined && appname !== null && appname !== "") {
|
if (appname !== undefined && appname !== null && appname !== "") {
|
||||||
parsedData["app_name"] = appname
|
parsedData["app_name"] = appname
|
||||||
}
|
}
|
||||||
@@ -315,169 +314,125 @@ const ChatBot = (props) => {
|
|||||||
|
|
||||||
setMessages(newmessages);
|
setMessages(newmessages);
|
||||||
|
|
||||||
//fetch(`http://localhost:8080/api/v1/conversation`, {
|
// Use streaming endpoint
|
||||||
fetch(`${globalUrl}/api/v1/conversation`, {
|
fetch(`${globalUrl}/api/v1/conversation/stream`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "text/event-stream",
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
body: JSON.stringify(parsedData),
|
body: JSON.stringify(parsedData),
|
||||||
})
|
})
|
||||||
.then((res) => res.text())
|
.then(async (response) => {
|
||||||
.then((resText) => {
|
if (!response.ok) {
|
||||||
setLoading(false)
|
const errorText = await response.text();
|
||||||
var data = {}
|
throw new Error(`Server returned status ${response.status}: ${errorText}`);
|
||||||
|
|
||||||
// JSON parse
|
|
||||||
try {
|
|
||||||
data = JSON.parse(resText);
|
|
||||||
} catch (e) {
|
|
||||||
console.log("Error parsing response as JSON: ", e);
|
|
||||||
|
|
||||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
|
||||||
newmessages.push({
|
|
||||||
"status": "received",
|
|
||||||
"message": resText,
|
|
||||||
"id": uuidv4(),
|
|
||||||
});
|
|
||||||
|
|
||||||
setMessages(newmessages);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let buffer = "";
|
||||||
|
let streamedText = "";
|
||||||
|
let newConversationId = "";
|
||||||
|
|
||||||
if (data.run_id !== undefined && data.run_id !== null && data.run_id !== "") {
|
// Keep waiting message until first chunk arrives
|
||||||
setRunId(data.run_id)
|
const streamMessageId = uuidv4();
|
||||||
}
|
let firstChunkReceived = false;
|
||||||
|
|
||||||
if (data.thread_id !== undefined && data.thread_id !== null && data.thread_id !== "") {
|
while (true) {
|
||||||
setThreadId(data.thread_id)
|
const { done, value } = await reader.read();
|
||||||
|
|
||||||
// Update URL if this is a new thread (not already in URL)
|
if (done) {
|
||||||
if (!urlThreadId && data.thread_id !== threadId) {
|
setLoading(false);
|
||||||
console.log("Navigating to new thread URL:", data.thread_id);
|
break;
|
||||||
navigate(`/chat/${data.thread_id}`, { replace: true });
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (data.success === undefined) {
|
buffer += decoder.decode(value, { stream: true });
|
||||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
const lines = buffer.split('\n');
|
||||||
newmessages.push({
|
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
||||||
"status": "received",
|
|
||||||
"message": resText,
|
|
||||||
"id": uuidv4(),
|
|
||||||
});
|
|
||||||
|
|
||||||
setMessages(newmessages);
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const jsonStr = line.replace('data: ', '');
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(jsonStr);
|
||||||
|
const eventType = data.type;
|
||||||
|
|
||||||
return;
|
if (eventType === "chunk") {
|
||||||
}
|
// On first chunk, remove waiting message and add streaming message
|
||||||
|
if (!firstChunkReceived) {
|
||||||
|
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
||||||
|
newmessages.push({
|
||||||
|
"id": streamMessageId,
|
||||||
|
"status": "received",
|
||||||
|
"message": "",
|
||||||
|
});
|
||||||
|
firstChunkReceived = true;
|
||||||
|
}
|
||||||
|
|
||||||
// authentication for app
|
// Append chunk to streamed text
|
||||||
// app validation (choose one)
|
streamedText += data.chunk || "";
|
||||||
const defaultMessage = `Default output. The feature you're interacting with may not have been implemented yet. Contact ${supportEmail} with a screenshot of this and your input please.`
|
|
||||||
|
// Update the message with accumulated text
|
||||||
var outputmessage = defaultMessage;
|
newmessages = newmessages.map(msg =>
|
||||||
var status = "received";
|
msg.id === streamMessageId
|
||||||
var action = ""
|
? { ...msg, message: streamedText }
|
||||||
if (data.success === false) {
|
: msg
|
||||||
if (data.reason !== undefined) {
|
);
|
||||||
outputmessage = data.reason
|
setMessages([...newmessages]);
|
||||||
}
|
|
||||||
|
|
||||||
status = "error"
|
} else if (eventType === "done") {
|
||||||
} else {
|
// Capture conversation ID for next turn
|
||||||
if (data.reason !== undefined) {
|
newConversationId = data.data;
|
||||||
outputmessage = data.reason
|
if (newConversationId) {
|
||||||
}
|
setConversationId(newConversationId);
|
||||||
}
|
|
||||||
|
// Update URL if this is a new conversation
|
||||||
|
if (!urlConversationId && newConversationId !== conversationId) {
|
||||||
|
navigate(`/chat/${newConversationId}`, { replace: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data.action !== undefined) {
|
} else if (eventType === "error") {
|
||||||
//console.log("Action is defined: ", data.action);
|
// Handle error event
|
||||||
action = data.action
|
const errorMsg = data.data || "An error occurred";
|
||||||
|
console.error("[Stream Error]:", errorMsg);
|
||||||
if (data.action === "app_authentication") {
|
|
||||||
// If success & app auth -> say auth success and show available labels
|
newmessages = newmessages.map(msg =>
|
||||||
// If !success & app auth -> do authentication
|
msg.id === streamMessageId
|
||||||
if (data.success === true) {
|
? { ...msg, status: "error", message: errorMsg }
|
||||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
: msg
|
||||||
// No action for this.
|
);
|
||||||
// "action": action,
|
setMessages([...newmessages]);
|
||||||
var appname = ""
|
setLoading(false);
|
||||||
if (data.apps !== undefined && data.apps !== null && data.apps.length > 0) {
|
return;
|
||||||
appname = data.apps[0].name.replaceAll("_", " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputmessage = `**Please specify which ${appname} action you want to use**: \n`
|
|
||||||
if (data.available_labels !== undefined && data.available_labels !== null && data.available_labels.length > 0) {
|
|
||||||
for (var i = 0; i < data.available_labels.length; i++) {
|
|
||||||
outputmessage += "* " + data.available_labels[i] + "\n"
|
|
||||||
}
|
}
|
||||||
outputmessage += "* Reauthenticate ([see auth](/admin?tab=app_auth))"
|
|
||||||
}
|
|
||||||
//Some opavailable actions: " + data.apps.map((app) => app.name).join(", ")
|
|
||||||
const parsedmessage = {
|
|
||||||
"status": status,
|
|
||||||
"message": outputmessage,
|
|
||||||
"id": uuidv4(),
|
|
||||||
"category": data.category,
|
|
||||||
|
|
||||||
"thread_id": data.thread_id,
|
} catch (e) {
|
||||||
"run_id": data.run_id,
|
console.error("[ERROR] Failed to parse SSE data:", e, jsonStr);
|
||||||
}
|
|
||||||
newmessages.push(parsedmessage);
|
|
||||||
setMessages(newmessages);
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (data.apps !== undefined) {
|
|
||||||
setInputAuth(data.apps)
|
|
||||||
|
|
||||||
setMessage(inputmsg);
|
|
||||||
|
|
||||||
setForceReauthentication(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (data.action === "select_category" || data.action === "select_app") {
|
|
||||||
console.log("[DEBUG] APP SELECTION! Should help them choose an app to use")
|
|
||||||
// Show a search field
|
|
||||||
setShowAppSearch(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
setLoading(false);
|
||||||
const parsedmessage = {
|
|
||||||
"status": status,
|
|
||||||
"message": outputmessage,
|
|
||||||
"id": uuidv4(),
|
|
||||||
"action": action,
|
|
||||||
"category": data.category,
|
|
||||||
|
|
||||||
"thread_id": data.thread_id,
|
|
||||||
"run_id": data.run_id,
|
|
||||||
}
|
|
||||||
newmessages.push(parsedmessage);
|
|
||||||
setMessages(newmessages);
|
|
||||||
console.log("New message: ", parsedmessage)
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
console.log("Problem: ", err);
|
console.log("Problem: ", err);
|
||||||
|
|
||||||
setMessage(message);
|
setMessage(inputmsg);
|
||||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
||||||
|
|
||||||
// Find the message with the sentId and change the status to error
|
// Add error message
|
||||||
newmessages.push({
|
newmessages.push({
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"message": message,
|
"message": inputmsg,
|
||||||
"error_message": "Failed to send: "+err,
|
"error_message": "Failed to send: "+err.message,
|
||||||
"id": sentId,
|
"id": sentId,
|
||||||
});
|
});
|
||||||
setMessages(newmessages);
|
setMessages([...newmessages]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -654,21 +609,21 @@ const ChatBot = (props) => {
|
|||||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius,}}>
|
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius,}}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
How many incidents did we get last week?
|
What is the difference between a trigger and an action?
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Answer the last email from Jim about the new project, and say we're on it
|
How do I create a new workflow?
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Is the IP 1.2.3.4 blocked? If not, block it.
|
Is there a way to mass abort executions of a specific workflow?
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1006,9 +961,9 @@ const ChatBot = (props) => {
|
|||||||
padding: "0 20px"
|
padding: "0 20px"
|
||||||
}}>
|
}}>
|
||||||
{[
|
{[
|
||||||
"How many incidents did we get last week?",
|
"What is the difference between a trigger and an action?",
|
||||||
"Answer the last email from Jim about the new project",
|
"How do I create a new workflow?",
|
||||||
"Is the IP 1.2.3.4 blocked? If not, block it."
|
"Is there a way to mass abort executions of a specific workflow?"
|
||||||
].map((sample, index) => (
|
].map((sample, index) => (
|
||||||
<Card key={index} style={{
|
<Card key={index} style={{
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const EditOrgTab = (props) => {
|
|||||||
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
const [orgRequest, setOrgRequest] = React.useState(true);
|
const [orgRequest, setOrgRequest] = React.useState(true);
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(users.length === 0) {
|
if(users.length === 0) {
|
||||||
getUsers();
|
getUsers();
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ const EditWorkflow = (props) => {
|
|||||||
<AutoAwesomeIcon style={{ marginRight: 8 }} />
|
<AutoAwesomeIcon style={{ marginRight: 8 }} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
AI Generate
|
AI Generate (beta)
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
|||||||
setCurrentSelectedTheme(userdata?.theme);
|
setCurrentSelectedTheme(userdata?.theme);
|
||||||
}
|
}
|
||||||
}, [userdata]);
|
}, [userdata]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const CustomPopper = (props) => {
|
const CustomPopper = (props) => {
|
||||||
|
|||||||
@@ -1910,7 +1910,7 @@ const ParsedAction = (props) => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography variant="body2" style={{margin: 3, }}>
|
<Typography variant="body2" style={{margin: 3, }}>
|
||||||
Rerun this action with results from previous executions. Built for testing individual actions in the middle of workflows.
|
Rerun this action with results from your previously selected workflow run. Allows testing individual action changes without rerunning the full workflow.
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
placement="top"
|
placement="top"
|
||||||
@@ -1922,8 +1922,8 @@ const ParsedAction = (props) => {
|
|||||||
marginTop: "auto",
|
marginTop: "auto",
|
||||||
marginBottom: "auto",
|
marginBottom: "auto",
|
||||||
height: 30,
|
height: 30,
|
||||||
marginLeft: 98,
|
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
|
marginLeft: 115,
|
||||||
}}
|
}}
|
||||||
disabled={autoCompleting}
|
disabled={autoCompleting}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -1934,7 +1934,7 @@ const ParsedAction = (props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PlayArrowIcon style={{marginRight: 5, }}/>
|
<CachedIcon style={{marginRight: 5, }}/>
|
||||||
Rerun
|
Rerun
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -2813,6 +2813,8 @@ const ParsedAction = (props) => {
|
|||||||
style: {
|
style: {
|
||||||
backgroundColor: theme.palette.platformColor,
|
backgroundColor: theme.palette.platformColor,
|
||||||
color: theme.palette.textColor,
|
color: theme.palette.textColor,
|
||||||
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
|
paddingRight: 20,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
filterOptions={(options, { inputValue }) => {
|
filterOptions={(options, { inputValue }) => {
|
||||||
@@ -3091,6 +3093,14 @@ const ParsedAction = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{apps.map((app, appIndex) => {
|
{apps.map((app, appIndex) => {
|
||||||
|
// Forces it into every category (for now)
|
||||||
|
// This is to make it possible to "use" shuffle for Singul natively
|
||||||
|
if (app.name === "Shuffle Tools") {
|
||||||
|
if (actionname == "Intel" || actionname == "Intel") {
|
||||||
|
app.categories = [actionname]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (app.categories === undefined || app.categories === null || app.categories.length === 0) {
|
if (app.categories === undefined || app.categories === null || app.categories.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -3176,7 +3186,7 @@ const ParsedAction = (props) => {
|
|||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
border: isAppSelected ? "3px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
border: isAppSelected ? "5px solid #86c142" : "2px solid rgba(255,255,255,0.6)",
|
||||||
}} />
|
}} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const Priority = (props) => {
|
|||||||
const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
|
const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
|
||||||
const { themeMode, supportEmail } = useContext(Context);
|
const { themeMode, supportEmail } = useContext(Context);
|
||||||
const theme = getTheme(themeMode);
|
const theme = getTheme(themeMode);
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
if (window.location.pathname === "/workflows") {
|
if (window.location.pathname === "/workflows") {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const SearchData = props => {
|
|||||||
// return null
|
// return null
|
||||||
//}
|
//}
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
// if (window.location.pathname === "/docs" || window.location.pathname === "/apps" || window.location.pathname === "/usecases" ) {
|
// if (window.location.pathname === "/docs" || window.location.pathname === "/apps" || window.location.pathname === "/usecases" ) {
|
||||||
// setModalOpen(false)
|
// setModalOpen(false)
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ const UsecaseSearch = (props) => {
|
|||||||
const [selectedAction, setSelectedAction] = React.useState({});
|
const [selectedAction, setSelectedAction] = React.useState({});
|
||||||
const [firstRequest, setFirstRequest] = React.useState(true);
|
const [firstRequest, setFirstRequest] = React.useState(true);
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
//const alert = useAlert()
|
//const alert = useAlert()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ const WelcomeForm = (props) => {
|
|||||||
const [clickdiff, setclickdiff] = useState(0);
|
const [clickdiff, setclickdiff] = useState(0);
|
||||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
//const alert = useAlert();
|
//const alert = useAlert();
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b
|
|||||||
const AppGrid = props => {
|
const AppGrid = props => {
|
||||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
|
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
||||||
//const [apps, setApps] = React.useState([]);
|
//const [apps, setApps] = React.useState([]);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const WorkflowTemplatePopup = (props) => {
|
|||||||
|
|
||||||
const [requestSent, setRequestSent] = React.useState(false)
|
const [requestSent, setRequestSent] = React.useState(false)
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (modalOpen !== true) {
|
if (modalOpen !== true) {
|
||||||
|
|||||||
+50
-17
@@ -62,7 +62,6 @@ const dividerColor = "rgb(225, 228, 232)";
|
|||||||
const hrefStyle = {
|
const hrefStyle = {
|
||||||
color: "rgba(255, 255, 255, 0.8)",
|
color: "rgba(255, 255, 255, 0.8)",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -387,6 +386,7 @@ export const CodeHandler = (props) => {
|
|||||||
minWidth: "50%",
|
minWidth: "50%",
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
// Have it inline
|
// Have it inline
|
||||||
borderRadius: theme.palette?.borderRadius,
|
borderRadius: theme.palette?.borderRadius,
|
||||||
@@ -1020,7 +1020,7 @@ const Docs = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchDocList = (resetCache = false) => {
|
const fetchDocList = (resetCache = false) => {
|
||||||
var url = `${globalUrl}/api/v1/docs`
|
var url = `${globalUrl}/api/v1/docs?resetCache=${resetCache}`
|
||||||
if (location.pathname.includes("/legal")) {
|
if (location.pathname.includes("/legal")) {
|
||||||
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
|
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
|
||||||
} else if (location.pathname.includes("/articles")) {
|
} else if (location.pathname.includes("/articles")) {
|
||||||
@@ -1047,16 +1047,16 @@ const Docs = (defaultprops) => {
|
|||||||
.catch((error) => { });
|
.catch((error) => { });
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchDoc = (docId) => {
|
const fetchDoc = (docId, resetCache = false) => {
|
||||||
if (docId === undefined) {
|
if (docId === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = `${globalUrl}/api/v1/docs/${docId}`
|
var url = `${globalUrl}/api/v1/docs/${docId}?resetCache=${resetCache}`
|
||||||
if (location.pathname.includes("/legal")) {
|
if (location.pathname.includes("/legal")) {
|
||||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal`
|
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal&resetCache=${resetCache}`
|
||||||
} else if (location.pathname.includes("/articles")) {
|
} else if (location.pathname.includes("/articles")) {
|
||||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
|
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles&resetCache=${resetCache}`
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
@@ -1082,7 +1082,7 @@ const Docs = (defaultprops) => {
|
|||||||
autoClose: 2000,
|
autoClose: 2000,
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate(`/articles/2.0_release`)
|
navigate(`/articles`)
|
||||||
}, 2000)
|
}, 2000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1130,7 +1130,8 @@ const Docs = (defaultprops) => {
|
|||||||
|
|
||||||
const handleResetCache = () => {
|
const handleResetCache = () => {
|
||||||
fetchDocList(true);
|
fetchDocList(true);
|
||||||
toast("Cache has been reset");
|
fetchDoc(props.match.params.key, true);
|
||||||
|
toast("Cache and list will be reset in a few seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstrequest) {
|
if (firstrequest) {
|
||||||
@@ -1343,6 +1344,21 @@ const Docs = (defaultprops) => {
|
|||||||
tr: TableRowRenderer,
|
tr: TableRowRenderer,
|
||||||
th: TableHeaderCellRenderer,
|
th: TableHeaderCellRenderer,
|
||||||
td: TableCellRenderer,
|
td: TableCellRenderer,
|
||||||
|
ul: ({ children }) => (
|
||||||
|
<Box component="ul" sx={{ my: 0.5, fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
ol: ({ children }) => (
|
||||||
|
<Box component="ol" sx={{ my: 0.5, fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
li: ({ children }) => (
|
||||||
|
<Typography component="li" sx={{ my: 0.5, color: "inherit", fontFamily: '"Segoe UI", Inter, sans-serif', fontSize: "17.6px" }}>
|
||||||
|
{children}
|
||||||
|
</Typography>
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1354,7 +1370,6 @@ const Docs = (defaultprops) => {
|
|||||||
|
|
||||||
const activeListItemStyle = {
|
const activeListItemStyle = {
|
||||||
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
|
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
|
||||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
|
||||||
borderLeft: "3px solid #f86a3e",
|
borderLeft: "3px solid #f86a3e",
|
||||||
paddingLeft: "13px", // Compensate for the border
|
paddingLeft: "13px", // Compensate for the border
|
||||||
};
|
};
|
||||||
@@ -1414,6 +1429,7 @@ const Docs = (defaultprops) => {
|
|||||||
<List style={{
|
<List style={{
|
||||||
listStyle: "none",
|
listStyle: "none",
|
||||||
paddingLeft: "0",
|
paddingLeft: "0",
|
||||||
|
paddingRight: isArticlePage ? 15 : undefined,
|
||||||
paddingTop: !isArticlePage ? "60px" : undefined,
|
paddingTop: !isArticlePage ? "60px" : undefined,
|
||||||
paddingBottom: !isArticlePage ? "30px" : undefined,
|
paddingBottom: !isArticlePage ? "30px" : undefined,
|
||||||
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
|
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
|
||||||
@@ -1523,15 +1539,32 @@ const Docs = (defaultprops) => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div style={IndexBar}>
|
<div style={IndexBar}>
|
||||||
{userdata?.support && isArticlePage && (
|
{userdata?.support && (
|
||||||
<Button
|
<Tooltip
|
||||||
variant="contained"
|
title="Wait about 5 minutes after updating the file on GitHub, then click to reset the cache. (Support only) "
|
||||||
color="secondary"
|
placement="top"
|
||||||
onClick={handleResetCache}
|
componentsProps={{
|
||||||
style={{marginBottom: "15px"}}
|
tooltip: {
|
||||||
|
sx: {
|
||||||
|
backgroundColor: "rgba(33, 33, 33, 1)",
|
||||||
|
color: "rgba(241, 241, 241, 1)",
|
||||||
|
fontSize: 14,
|
||||||
|
border: "1px solid rgba(73, 73, 73, 1)",
|
||||||
|
fontFamily: theme?.typography?.fontFamily,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
arrow
|
||||||
>
|
>
|
||||||
Reset Cache
|
<Button
|
||||||
</Button>
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
onClick={handleResetCache}
|
||||||
|
style={{marginBottom: "15px"}}
|
||||||
|
>
|
||||||
|
Reset Cache
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{tocLines.length > 0 ?
|
{tocLines.length > 0 ?
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ const NewDashboard = (props) => {
|
|||||||
const STATIC_TIME_PERCENT = 'TBD'
|
const STATIC_TIME_PERCENT = 'TBD'
|
||||||
const STATIC_MONEY_PERCENT = 'TBD'
|
const STATIC_MONEY_PERCENT = 'TBD'
|
||||||
const kpis = [
|
const kpis = [
|
||||||
{ value: timeFmt.display, title: timeFmt.title, label: 'Time saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true},
|
//{ value: timeFmt.display, title: timeFmt.title, label: 'Time saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true},
|
||||||
{ value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, },
|
//{ value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, },
|
||||||
{ value: String(unreadCount), label: 'Total errors', icon: <ErrorOutlineIcon sx={{ color: '#f87171', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#f87171' },
|
{ value: String(unreadCount), label: 'Total errors', icon: <ErrorOutlineIcon sx={{ color: '#f87171', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#f87171' },
|
||||||
{ value: String(readCount), label: 'Errors resolved', icon: <TaskAltIcon sx={{ color: '#5cc879', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#5cc879' },
|
{ value: String(readCount), label: 'Errors resolved', icon: <TaskAltIcon sx={{ color: '#5cc879', fontSize: 34, opacity: 0.9 }} />, percentage: "", color: '#5cc879' },
|
||||||
];
|
];
|
||||||
@@ -131,12 +131,12 @@ const NewDashboard = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const anyLoading =
|
const anyLoading =
|
||||||
loadingSfw ||
|
//loadingSfw ||
|
||||||
loadingRot ||
|
//loadingRot ||
|
||||||
loadingNoti ||
|
loadingNoti ||
|
||||||
loadingSelectedOrgStats ||
|
loadingSelectedOrgStats //||
|
||||||
!selectedOrganization ||
|
!selectedOrganization ||
|
||||||
!selectedOrgForStats;
|
//!selectedOrgForStats;
|
||||||
setShowOverlay(anyLoading);
|
setShowOverlay(anyLoading);
|
||||||
}, [
|
}, [
|
||||||
loadingSfw,
|
loadingSfw,
|
||||||
@@ -397,6 +397,7 @@ const NewDashboard = (props) => {
|
|||||||
headerSubtitle="Complete these steps to start seeing insights."
|
headerSubtitle="Complete these steps to start seeing insights."
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showOverlay && (
|
{showOverlay && (
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'rgba(17,17,17,0.6)', backdropFilter: 'blur(2px)', zIndex: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 12 }}>
|
<div style={{ position: 'absolute', inset: 0, background: 'rgba(17,17,17,0.6)', backdropFilter: 'blur(2px)', zIndex: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 12 }}>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
||||||
@@ -405,6 +406,7 @@ const NewDashboard = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Header / Greeting */}
|
{/* Header / Greeting */}
|
||||||
<Box style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '8px 0 16px 0' }}>
|
<Box style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '8px 0 16px 0' }}>
|
||||||
<Typography variant="h5">{`${getGreeting()}, ${displayName ?? 'User'}!`}</Typography>
|
<Typography variant="h5">{`${getGreeting()}, ${displayName ?? 'User'}!`}</Typography>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const SetAuthentication = (props) => {
|
|||||||
const [loadFail, setLoadFail] = useState("");
|
const [loadFail, setLoadFail] = useState("");
|
||||||
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
//const alert = useAlert();
|
//const alert = useAlert();
|
||||||
|
|
||||||
const parseIncomingOpenapiData = (data) => {
|
const parseIncomingOpenapiData = (data) => {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const Welcome = (props) => {
|
|||||||
}
|
}
|
||||||
}, [activeStep])
|
}, [activeStep])
|
||||||
|
|
||||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
const [steps, setSteps] = useState([
|
const [steps, setSteps] = useState([
|
||||||
"Help us get to know you",
|
"Help us get to know you",
|
||||||
"Find your Apps",
|
"Find your Apps",
|
||||||
|
|||||||
Reference in New Issue
Block a user