Merge branch 'main' into nightly
This commit is contained in:
@@ -20,19 +20,19 @@ jobs:
|
||||
include:
|
||||
- app: frontend
|
||||
path: frontend
|
||||
version: 2.1.2
|
||||
version: 2.1.3
|
||||
experimental: true
|
||||
- app: backend
|
||||
path: backend
|
||||
version: 2.1.2
|
||||
version: 2.1.3
|
||||
experimental: true
|
||||
- app: orborus
|
||||
path: functions/onprem/orborus
|
||||
version: 2.1.2
|
||||
version: 2.1.3
|
||||
experimental: true
|
||||
- app: worker
|
||||
path: functions/onprem/worker
|
||||
version: 2.1.2
|
||||
version: 2.1.3
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -1129,7 +1129,7 @@ const AppFramework = (props) => {
|
||||
}, [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;
|
||||
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@ const AppSelection = props => {
|
||||
document.title = "Choose your apps"
|
||||
const ref = useRef()
|
||||
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(() => {
|
||||
if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||
|
||||
+115
-160
@@ -43,6 +43,8 @@ const ChatBot = (props) => {
|
||||
const [appname, setAppname] = useState("");
|
||||
const [threadId, setThreadId] = useState("");
|
||||
const [runId, setRunId] = useState("");
|
||||
const [responseId, setResponseId] = useState("");
|
||||
const [conversationId, setConversationId] = useState("");
|
||||
|
||||
// New state for thread management
|
||||
const [isLoadingThread, setIsLoadingThread] = useState(false);
|
||||
@@ -54,7 +56,7 @@ const ChatBot = (props) => {
|
||||
const [showAppSearch, setShowAppSearch] = useState(false);
|
||||
|
||||
// Get thread ID from URL params
|
||||
const { threadId: urlThreadId } = useParams();
|
||||
const { conversationId: urlConversationId } = useParams();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const waitingMsg = "Processing..."
|
||||
@@ -93,24 +95,24 @@ const ChatBot = (props) => {
|
||||
}
|
||||
}, [appname])
|
||||
|
||||
// Load existing thread if threadId is in URL
|
||||
// Load existing conversation if conversationId is in URL
|
||||
useEffect(() => {
|
||||
if (urlThreadId && urlThreadId !== threadId) {
|
||||
loadExistingThread(urlThreadId);
|
||||
if (urlConversationId && urlConversationId !== conversationId) {
|
||||
loadExistingThread(urlConversationId);
|
||||
}
|
||||
}, [urlThreadId]);
|
||||
}, [urlConversationId]);
|
||||
|
||||
const loadExistingThread = (threadIdToLoad) => {
|
||||
const loadExistingThread = (conversationIdToLoad) => {
|
||||
setIsLoadingThread(true);
|
||||
setThreadError("");
|
||||
|
||||
fetch(`${globalUrl}/api/v1/conversation/thread`, {
|
||||
fetch(`${globalUrl}/api/v1/conversation/history`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ thread_id: threadIdToLoad }),
|
||||
body: JSON.stringify({ conversation_id: conversationIdToLoad }),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 403) {
|
||||
@@ -138,22 +140,22 @@ const ChatBot = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set thread data - this is crucial for continuing the conversation
|
||||
setThreadId(data.thread_id);
|
||||
console.log("Loaded existing thread:", data.thread_id);
|
||||
// Set conversation data - this is crucial for continuing the conversation
|
||||
setConversationId(data.conversation_id);
|
||||
|
||||
// Transform API message format to UI format
|
||||
const transformedMessages = (data.messages || []).map((msg, index) => ({
|
||||
id: `${data.thread_id}_${index}`,
|
||||
id: `${data.conversation_id}_${index}`,
|
||||
status: msg.role === "user" ? "sent" : "received",
|
||||
message: msg.content,
|
||||
timestamp: msg.timestamp
|
||||
}));
|
||||
|
||||
setMessages(transformedMessages);
|
||||
setThreadOrgId(data.thread_org_id);
|
||||
setThreadOrgId(data.org_id);
|
||||
setIsActiveOrg(data.is_active_org);
|
||||
|
||||
// Disable chat if not in active org
|
||||
if (!data.is_active_org) {
|
||||
setChatDisabled(true);
|
||||
} else {
|
||||
@@ -260,12 +262,9 @@ const ChatBot = (props) => {
|
||||
const sentId = uuidv4();
|
||||
var parsedData = {
|
||||
"query": inputmsg,
|
||||
"thread_id": threadId,
|
||||
"run_id": runId,
|
||||
"conversation_id": conversationId,
|
||||
}
|
||||
|
||||
console.log("Sending message with thread_id:", threadId);
|
||||
|
||||
if (appname !== undefined && appname !== null && appname !== "") {
|
||||
parsedData["app_name"] = appname
|
||||
}
|
||||
@@ -315,169 +314,125 @@ const ChatBot = (props) => {
|
||||
|
||||
setMessages(newmessages);
|
||||
|
||||
//fetch(`http://localhost:8080/api/v1/conversation`, {
|
||||
fetch(`${globalUrl}/api/v1/conversation`, {
|
||||
// Use streaming endpoint
|
||||
fetch(`${globalUrl}/api/v1/conversation/stream`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "text/event-stream",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(parsedData),
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((resText) => {
|
||||
setLoading(false)
|
||||
var data = {}
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Server returned status ${response.status}: ${errorText}`);
|
||||
}
|
||||
|
||||
// JSON parse
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
let streamedText = "";
|
||||
let newConversationId = "";
|
||||
|
||||
// Keep waiting message until first chunk arrives
|
||||
const streamMessageId = uuidv4();
|
||||
let firstChunkReceived = false;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
setLoading(false);
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const jsonStr = line.replace('data: ', '');
|
||||
try {
|
||||
data = JSON.parse(resText);
|
||||
const data = JSON.parse(jsonStr);
|
||||
const eventType = data.type;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Append chunk to streamed text
|
||||
streamedText += data.chunk || "";
|
||||
|
||||
// Update the message with accumulated text
|
||||
newmessages = newmessages.map(msg =>
|
||||
msg.id === streamMessageId
|
||||
? { ...msg, message: streamedText }
|
||||
: msg
|
||||
);
|
||||
setMessages([...newmessages]);
|
||||
|
||||
} else if (eventType === "done") {
|
||||
// Capture conversation ID for next turn
|
||||
newConversationId = data.data;
|
||||
if (newConversationId) {
|
||||
setConversationId(newConversationId);
|
||||
|
||||
// Update URL if this is a new conversation
|
||||
if (!urlConversationId && newConversationId !== conversationId) {
|
||||
navigate(`/chat/${newConversationId}`, { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
} else if (eventType === "error") {
|
||||
// Handle error event
|
||||
const errorMsg = data.data || "An error occurred";
|
||||
console.error("[Stream Error]:", errorMsg);
|
||||
|
||||
newmessages = newmessages.map(msg =>
|
||||
msg.id === streamMessageId
|
||||
? { ...msg, status: "error", message: errorMsg }
|
||||
: msg
|
||||
);
|
||||
setMessages([...newmessages]);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
} 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;
|
||||
console.error("[ERROR] Failed to parse SSE data:", e, jsonStr);
|
||||
}
|
||||
|
||||
|
||||
if (data.run_id !== undefined && data.run_id !== null && data.run_id !== "") {
|
||||
setRunId(data.run_id)
|
||||
}
|
||||
|
||||
if (data.thread_id !== undefined && data.thread_id !== null && data.thread_id !== "") {
|
||||
setThreadId(data.thread_id)
|
||||
|
||||
// Update URL if this is a new thread (not already in URL)
|
||||
if (!urlThreadId && data.thread_id !== threadId) {
|
||||
console.log("Navigating to new thread URL:", data.thread_id);
|
||||
navigate(`/chat/${data.thread_id}`, { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (data.success === undefined) {
|
||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
||||
newmessages.push({
|
||||
"status": "received",
|
||||
"message": resText,
|
||||
"id": uuidv4(),
|
||||
});
|
||||
|
||||
setMessages(newmessages);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// authentication for app
|
||||
// app validation (choose one)
|
||||
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.`
|
||||
|
||||
var outputmessage = defaultMessage;
|
||||
var status = "received";
|
||||
var action = ""
|
||||
if (data.success === false) {
|
||||
if (data.reason !== undefined) {
|
||||
outputmessage = data.reason
|
||||
}
|
||||
|
||||
status = "error"
|
||||
} else {
|
||||
if (data.reason !== undefined) {
|
||||
outputmessage = data.reason
|
||||
}
|
||||
}
|
||||
|
||||
if (data.action !== undefined) {
|
||||
//console.log("Action is defined: ", data.action);
|
||||
action = data.action
|
||||
|
||||
if (data.action === "app_authentication") {
|
||||
// If success & app auth -> say auth success and show available labels
|
||||
// If !success & app auth -> do authentication
|
||||
if (data.success === true) {
|
||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
||||
// No action for this.
|
||||
// "action": action,
|
||||
var appname = ""
|
||||
if (data.apps !== undefined && data.apps !== null && data.apps.length > 0) {
|
||||
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,
|
||||
"run_id": data.run_id,
|
||||
}
|
||||
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);
|
||||
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)
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false)
|
||||
console.log("Problem: ", err);
|
||||
|
||||
setMessage(message);
|
||||
setMessage(inputmsg);
|
||||
newmessages = newmessages.filter((msg) => msg.message !== waitingMsg);
|
||||
|
||||
// Find the message with the sentId and change the status to error
|
||||
// Add error message
|
||||
newmessages.push({
|
||||
"status": "error",
|
||||
"message": message,
|
||||
"error_message": "Failed to send: "+err,
|
||||
"message": inputmsg,
|
||||
"error_message": "Failed to send: "+err.message,
|
||||
"id": sentId,
|
||||
});
|
||||
setMessages(newmessages);
|
||||
setMessages([...newmessages]);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -654,21 +609,21 @@ const ChatBot = (props) => {
|
||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius,}}>
|
||||
<CardContent>
|
||||
<Typography variant="h6">
|
||||
How many incidents did we get last week?
|
||||
What is the difference between a trigger and an action?
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
||||
<CardContent>
|
||||
<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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card style={{backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, marginTop: 10, }}>
|
||||
<CardContent>
|
||||
<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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -1006,9 +961,9 @@ const ChatBot = (props) => {
|
||||
padding: "0 20px"
|
||||
}}>
|
||||
{[
|
||||
"How many incidents did we get last week?",
|
||||
"Answer the last email from Jim about the new project",
|
||||
"Is the IP 1.2.3.4 blocked? If not, block it."
|
||||
"What is the difference between a trigger and an action?",
|
||||
"How do I create a new workflow?",
|
||||
"Is there a way to mass abort executions of a specific workflow?"
|
||||
].map((sample, index) => (
|
||||
<Card key={index} style={{
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
|
||||
@@ -30,7 +30,7 @@ const EditOrgTab = (props) => {
|
||||
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||
const [users, setUsers] = React.useState([]);
|
||||
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(() => {
|
||||
if(users.length === 0) {
|
||||
getUsers();
|
||||
|
||||
@@ -633,7 +633,7 @@ const EditWorkflow = (props) => {
|
||||
<AutoAwesomeIcon style={{ marginRight: 8 }} />
|
||||
)}
|
||||
|
||||
AI Generate
|
||||
AI Generate (beta)
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1910,7 +1910,7 @@ const ParsedAction = (props) => {
|
||||
<Tooltip
|
||||
title={
|
||||
<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>
|
||||
}
|
||||
placement="top"
|
||||
@@ -1922,8 +1922,8 @@ const ParsedAction = (props) => {
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
height: 30,
|
||||
marginLeft: 98,
|
||||
textTransform: "none",
|
||||
marginLeft: 115,
|
||||
}}
|
||||
disabled={autoCompleting}
|
||||
onClick={() => {
|
||||
@@ -1934,7 +1934,7 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PlayArrowIcon style={{marginRight: 5, }}/>
|
||||
<CachedIcon style={{marginRight: 5, }}/>
|
||||
Rerun
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@@ -2813,6 +2813,8 @@ const ParsedAction = (props) => {
|
||||
style: {
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
color: theme.palette.textColor,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
paddingRight: 20,
|
||||
},
|
||||
}}
|
||||
filterOptions={(options, { inputValue }) => {
|
||||
@@ -3091,6 +3093,14 @@ const ParsedAction = (props) => {
|
||||
</div>
|
||||
|
||||
{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) {
|
||||
return null
|
||||
}
|
||||
@@ -3176,7 +3186,7 @@ const ParsedAction = (props) => {
|
||||
marginRight: 5,
|
||||
borderRadius: 5,
|
||||
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>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ const Priority = (props) => {
|
||||
const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
|
||||
const { themeMode, supportEmail } = useContext(Context);
|
||||
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();
|
||||
|
||||
if (window.location.pathname === "/workflows") {
|
||||
|
||||
@@ -77,7 +77,7 @@ const SearchData = props => {
|
||||
// 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" ) {
|
||||
// setModalOpen(false)
|
||||
// }
|
||||
|
||||
@@ -349,7 +349,7 @@ const UsecaseSearch = (props) => {
|
||||
const [selectedAction, setSelectedAction] = React.useState({});
|
||||
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()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -161,7 +161,7 @@ const WelcomeForm = (props) => {
|
||||
const [clickdiff, setclickdiff] = useState(0);
|
||||
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();
|
||||
let navigate = useNavigate();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b
|
||||
const AppGrid = 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 xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
|
||||
@@ -47,7 +47,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
|
||||
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();
|
||||
useEffect(() => {
|
||||
if (modalOpen !== true) {
|
||||
|
||||
+43
-10
@@ -62,7 +62,6 @@ const dividerColor = "rgb(225, 228, 232)";
|
||||
const hrefStyle = {
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
textDecoration: "none",
|
||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
||||
};
|
||||
|
||||
|
||||
@@ -387,6 +386,7 @@ export const CodeHandler = (props) => {
|
||||
minWidth: "50%",
|
||||
maxWidth: "100%",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
whiteSpace: "pre-wrap",
|
||||
overflowY: "auto",
|
||||
// Have it inline
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
@@ -1020,7 +1020,7 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
const fetchDocList = (resetCache = false) => {
|
||||
var url = `${globalUrl}/api/v1/docs`
|
||||
var url = `${globalUrl}/api/v1/docs?resetCache=${resetCache}`
|
||||
if (location.pathname.includes("/legal")) {
|
||||
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
|
||||
} else if (location.pathname.includes("/articles")) {
|
||||
@@ -1047,16 +1047,16 @@ const Docs = (defaultprops) => {
|
||||
.catch((error) => { });
|
||||
};
|
||||
|
||||
const fetchDoc = (docId) => {
|
||||
const fetchDoc = (docId, resetCache = false) => {
|
||||
if (docId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
var url = `${globalUrl}/api/v1/docs/${docId}`
|
||||
var url = `${globalUrl}/api/v1/docs/${docId}?resetCache=${resetCache}`
|
||||
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")) {
|
||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
|
||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles&resetCache=${resetCache}`
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
@@ -1082,7 +1082,7 @@ const Docs = (defaultprops) => {
|
||||
autoClose: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate(`/articles/2.0_release`)
|
||||
navigate(`/articles`)
|
||||
}, 2000)
|
||||
return
|
||||
}
|
||||
@@ -1130,7 +1130,8 @@ const Docs = (defaultprops) => {
|
||||
|
||||
const handleResetCache = () => {
|
||||
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) {
|
||||
@@ -1343,6 +1344,21 @@ const Docs = (defaultprops) => {
|
||||
tr: TableRowRenderer,
|
||||
th: TableHeaderCellRenderer,
|
||||
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 = {
|
||||
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
|
||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
||||
borderLeft: "3px solid #f86a3e",
|
||||
paddingLeft: "13px", // Compensate for the border
|
||||
};
|
||||
@@ -1414,6 +1429,7 @@ const Docs = (defaultprops) => {
|
||||
<List style={{
|
||||
listStyle: "none",
|
||||
paddingLeft: "0",
|
||||
paddingRight: isArticlePage ? 15 : undefined,
|
||||
paddingTop: !isArticlePage ? "60px" : undefined,
|
||||
paddingBottom: !isArticlePage ? "30px" : undefined,
|
||||
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
|
||||
@@ -1523,7 +1539,23 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
</div>
|
||||
<div style={IndexBar}>
|
||||
{userdata?.support && isArticlePage && (
|
||||
{userdata?.support && (
|
||||
<Tooltip
|
||||
title="Wait about 5 minutes after updating the file on GitHub, then click to reset the cache. (Support only) "
|
||||
placement="top"
|
||||
componentsProps={{
|
||||
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
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
@@ -1532,6 +1564,7 @@ const Docs = (defaultprops) => {
|
||||
>
|
||||
Reset Cache
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{tocLines.length > 0 ?
|
||||
(
|
||||
|
||||
@@ -109,8 +109,8 @@ const NewDashboard = (props) => {
|
||||
const STATIC_TIME_PERCENT = 'TBD'
|
||||
const STATIC_MONEY_PERCENT = 'TBD'
|
||||
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: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: <TrendingUpIcon sx={{ color: '#5cc879', fontSize: 34 }} />, percentage: STATIC_MONEY_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: 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' },
|
||||
];
|
||||
@@ -131,12 +131,12 @@ const NewDashboard = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const anyLoading =
|
||||
loadingSfw ||
|
||||
loadingRot ||
|
||||
//loadingSfw ||
|
||||
//loadingRot ||
|
||||
loadingNoti ||
|
||||
loadingSelectedOrgStats ||
|
||||
loadingSelectedOrgStats //||
|
||||
!selectedOrganization ||
|
||||
!selectedOrgForStats;
|
||||
//!selectedOrgForStats;
|
||||
setShowOverlay(anyLoading);
|
||||
}, [
|
||||
loadingSfw,
|
||||
@@ -397,6 +397,7 @@ const NewDashboard = (props) => {
|
||||
headerSubtitle="Complete these steps to start seeing insights."
|
||||
/>
|
||||
)}
|
||||
|
||||
{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={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
||||
@@ -405,6 +406,7 @@ const NewDashboard = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header / Greeting */}
|
||||
<Box style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '8px 0 16px 0' }}>
|
||||
<Typography variant="h5">{`${getGreeting()}, ${displayName ?? 'User'}!`}</Typography>
|
||||
|
||||
@@ -25,7 +25,7 @@ const SetAuthentication = (props) => {
|
||||
const [loadFail, setLoadFail] = 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 parseIncomingOpenapiData = (data) => {
|
||||
|
||||
@@ -47,7 +47,7 @@ const Welcome = (props) => {
|
||||
}
|
||||
}, [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([
|
||||
"Help us get to know you",
|
||||
"Find your Apps",
|
||||
|
||||
Reference in New Issue
Block a user