diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index f5098788..9f948ad4 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -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 diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 00c93b50..c0cd74d9 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -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 diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index d7cfecbd..d8e7f5e4 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -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) { diff --git a/frontend/src/components/ChatBot.jsx b/frontend/src/components/ChatBot.jsx index 63a7adb0..21f744ac 100644 --- a/frontend/src/components/ChatBot.jsx +++ b/frontend/src/components/ChatBot.jsx @@ -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 = {} - - // 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; + .then(async (response) => { + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Server returned status ${response.status}: ${errorText}`); } + 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 !== "") { - setRunId(data.run_id) - } + // Keep waiting message until first chunk arrives + const streamMessageId = uuidv4(); + let firstChunkReceived = false; - if (data.thread_id !== undefined && data.thread_id !== null && data.thread_id !== "") { - setThreadId(data.thread_id) + while (true) { + const { done, value } = await reader.read(); - // 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 (done) { + setLoading(false); + break; } - } - if (data.success === undefined) { - newmessages = newmessages.filter((msg) => msg.message !== waitingMsg); - newmessages.push({ - "status": "received", - "message": resText, - "id": uuidv4(), - }); + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() || ""; // Keep incomplete line in buffer - 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 - // 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 - } + // 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]); - status = "error" - } else { - if (data.reason !== undefined) { - outputmessage = data.reason - } - } + } 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 }); + } + } - 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" + } 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; } - 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) + } catch (e) { + console.error("[ERROR] Failed to parse SSE data:", e, jsonStr); } } - } 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) => { - 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, and say we're on it + 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? @@ -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) => ( { 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(); diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 0050411b..aceafe03 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -633,7 +633,7 @@ const EditWorkflow = (props) => { )} - AI Generate + AI Generate (beta) diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index bf8fbd9b..c6c808b1 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -120,7 +120,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { setCurrentSelectedTheme(userdata?.theme); } }, [userdata]); - + const CustomPopper = (props) => { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 104d9e40..570bd566 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1910,7 +1910,7 @@ const ParsedAction = (props) => { - 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. } 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) => { } }} > - + Rerun @@ -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) => { {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)", }} /> diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 9d4d4444..d3f82c48 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -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") { diff --git a/frontend/src/components/SearchData.jsx b/frontend/src/components/SearchData.jsx index 4534df73..01f619bf 100644 --- a/frontend/src/components/SearchData.jsx +++ b/frontend/src/components/SearchData.jsx @@ -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) // } diff --git a/frontend/src/components/UsecaseSearch.jsx b/frontend/src/components/UsecaseSearch.jsx index 2f8dbb87..ff90bc68 100644 --- a/frontend/src/components/UsecaseSearch.jsx +++ b/frontend/src/components/UsecaseSearch.jsx @@ -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(() => { diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index 2c4d37c0..41d45210 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -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(); diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index b421e208..e4a34134 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -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([]); diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 40700c2a..de9f08dc 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -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) { diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 6a427016..74e8ee3b 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -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 }) => ( + + {children} + + ), + ol: ({ children }) => ( + + {children} + + ), + li: ({ children }) => ( + + {children} + + ), } @@ -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) => { { }
- {userdata?.support && isArticlePage && ( - + + )} {tocLines.length > 0 ? ( diff --git a/frontend/src/views/NewDashboard.jsx b/frontend/src/views/NewDashboard.jsx index 2bbdc8b6..bf8b713c 100644 --- a/frontend/src/views/NewDashboard.jsx +++ b/frontend/src/views/NewDashboard.jsx @@ -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: , percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true}, - { value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: , percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, }, + //{ value: timeFmt.display, title: timeFmt.title, label: 'Time saved', icon: , percentage: STATIC_TIME_PERCENT, color: '#5cc879', disabled: true}, + //{ value: formatCurrencyCompact(totals.moneySavedDollars), label: 'Money saved', icon: , percentage: STATIC_MONEY_PERCENT, color: '#5cc879', disabled: true, }, { value: String(unreadCount), label: 'Total errors', icon: , percentage: "", color: '#f87171' }, { value: String(readCount), label: 'Errors resolved', icon: , 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 && (
@@ -405,6 +406,7 @@ const NewDashboard = (props) => {
)} + {/* Header / Greeting */} {`${getGreeting()}, ${displayName ?? 'User'}!`} diff --git a/frontend/src/views/UpdateAuthentication.jsx b/frontend/src/views/UpdateAuthentication.jsx index a87fb192..9f4702ea 100644 --- a/frontend/src/views/UpdateAuthentication.jsx +++ b/frontend/src/views/UpdateAuthentication.jsx @@ -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) => { diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index 5f4409fd..0af565e0 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -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",