Merge branch 'main' into nightly

This commit is contained in:
Yash Singh
2026-02-10 18:32:12 +05:30
committed by GitHub
18 changed files with 194 additions and 193 deletions
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -633,7 +633,7 @@ const EditWorkflow = (props) => {
<AutoAwesomeIcon style={{ marginRight: 8 }} />
)}
AI Generate
AI Generate (beta)
</Button>
</span>
</Tooltip>
+14 -4
View File
@@ -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>
+1 -1
View File
@@ -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") {
+1 -1
View File
@@ -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)
// }
+1 -1
View File
@@ -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(() => {
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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
View File
@@ -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 ?
(
+8 -6
View File
@@ -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>
+1 -1
View File
@@ -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) => {
+1 -1
View File
@@ -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",