Merge branch 'nightly' of https://github.com/shuffle/shuffle into nightly

This commit is contained in:
Frikky
2026-02-10 14:28:16 +01:00
24 changed files with 289 additions and 281 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) {
-2
View File
@@ -55,7 +55,6 @@ import {
OpenInNew as OpenInNewIcon,
CloudDownload as CloudDownloadIcon,
Description as DescriptionIcon,
Polymer as PolymerIcon,
CheckCircle as CheckCircleIcon,
Close as CloseIcon,
Apps as AppsIcon,
@@ -63,7 +62,6 @@ import {
Cached as CachedIcon,
AccessibilityNew as AccessibilityNewIcon,
Lock as LockIcon,
Eco as EcoIcon,
Schedule as ScheduleIcon,
Cloud as CloudIcon,
Business as BusinessIcon,
+104 -149
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 = {}
// 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) => {
<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>
+1 -1
View File
@@ -120,7 +120,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, SHUFFLE_V
setCurrentSelectedTheme(userdata?.theme);
}
}, [userdata]);
const CustomPopper = (props) => {
+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>
@@ -70,7 +70,6 @@ import {
Cached as CachedIcon,
DirectionsRun as DirectionsRunIcon,
Add as AddIcon,
Polymer as PolymerIcon,
FormatListNumbered as FormatListNumberedIcon,
Create as CreateIcon,
PlayArrow as PlayArrowIcon,
+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
@@ -44,7 +44,7 @@ import {
Send as SendIcon,
} from '@mui/icons-material';
import { DataGrid, GridValueGetterParams } from '@mui/x-data-grid'
import { DataGrid } from '@mui/x-data-grid'
import {
Search as SearchIcon,
} from "@mui/icons-material";
+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)
// }
@@ -1597,7 +1597,7 @@ const CodeEditor = (props) => {
if (e.srcElement.className === "ace_content") {
console.log("DRAG STOP IN CONTENT!", e.srcElement.className)
const usedposition = e.offsetY
let usedposition = e.offsetY
if (usedposition === undefined || usedposition === null) {
toast.info(`Error: LayerY is undefined or null. Please contact ${supportEmail}`)
return
+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) {
+93 -82
View File
@@ -153,10 +153,20 @@ const theme = createTheme(adaptV4Theme({
export default theme;
export const getTheme = (themeMode, brandColor) =>
createTheme({
export const getTheme = (themeMode, brandColor) => {
// Handle "system" mode by checking user's system preference
let resolvedMode = themeMode;
if (themeMode === "system" || !themeMode) {
resolvedMode = window?.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
}
// Ensure mode is only "dark" or "light"
if (resolvedMode !== "dark" && resolvedMode !== "light") {
resolvedMode = "dark";
}
return createTheme({
palette: {
mode: themeMode,
mode: resolvedMode,
main: brandColor || "#FF8544",
primary: {
main: brandColor || "#FF8544",
@@ -167,35 +177,35 @@ export const getTheme = (themeMode, brandColor) =>
contrastText:"#000000",
},
text: {
primary: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
secondary: themeMode === "dark" ? "#9E9E9E" : "#616161",
primary: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
secondary: resolvedMode === "dark" ? "#9E9E9E" : "#616161",
},
type: themeMode,
inputColor: themeMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)",
textColor: themeMode === "dark" ? "#F1F1F1" : "#1A1A1A",
textPrimary: themeMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)",
surfaceColor: themeMode === "dark" ? "#27292d" : "#EFEFEF",
platformColor: themeMode === "dark" ? "#212121" : "#ffffff",
backgroundColor: themeMode === "dark" ? "#1a1a1a" : "#f1f1f1",
cytoscapeBackgroundColor: themeMode === "dark" ? "#161616" : "#f5f5f5",
distributionColor: themeMode === "dark" ? "#40E0D0" : "#008080",
cardBackgroundColor: themeMode === "dark" ? "#1e1e1e" : "#eaeaea",
cardHoverColor: themeMode === "dark" ? "#323232" : "#F0F0F0",
hoverColor: themeMode === "dark" ? "#323232" : "#D6D6D6",
usecaseCardColor: themeMode === "dark" ? "#2f2f2f" : "rgba(245, 245, 245, 1)",
usecaseCardHoverColor: themeMode === "dark" ? "#2F2F2F" : "rgba(245, 245, 245, 1)",
usecaseDialogFieldColor: themeMode === "dark" ? "#2B2B2B" : "#F5F5F5",
accentColor: themeMode === "dark" ? "#ff8544" : "#ff8544",
green: themeMode === "dark" ? "#5cc879" : "#008000",
defaultBorder: themeMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC',
type: resolvedMode,
inputColor: resolvedMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)",
textColor: resolvedMode === "dark" ? "#F1F1F1" : "#1A1A1A",
textPrimary: resolvedMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)",
surfaceColor: resolvedMode === "dark" ? "#27292d" : "#EFEFEF",
platformColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
backgroundColor: resolvedMode === "dark" ? "#1a1a1a" : "#f1f1f1",
cytoscapeBackgroundColor: resolvedMode === "dark" ? "#161616" : "#f5f5f5",
distributionColor: resolvedMode === "dark" ? "#40E0D0" : "#008080",
cardBackgroundColor: resolvedMode === "dark" ? "#1e1e1e" : "#eaeaea",
cardHoverColor: resolvedMode === "dark" ? "#323232" : "#F0F0F0",
hoverColor: resolvedMode === "dark" ? "#323232" : "#D6D6D6",
usecaseCardColor: resolvedMode === "dark" ? "#2f2f2f" : "rgba(245, 245, 245, 1)",
usecaseCardHoverColor: resolvedMode === "dark" ? "#2F2F2F" : "rgba(245, 245, 245, 1)",
usecaseDialogFieldColor: resolvedMode === "dark" ? "#2B2B2B" : "#F5F5F5",
accentColor: resolvedMode === "dark" ? "#ff8544" : "#ff8544",
green: resolvedMode === "dark" ? "#5cc879" : "#008000",
defaultBorder: resolvedMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC',
linkColor: brandColor === "#ff8544" ? "#f86a3e" : brandColor,
slateGrayColor: themeMode === "dark" ? "#494949" : "#CCCCCC",
parsedAppPaperColor: themeMode === "dark" ? "#2f2f2f" : "#CCCCCC",
slateGrayColor: resolvedMode === "dark" ? "#494949" : "#CCCCCC",
parsedAppPaperColor: resolvedMode === "dark" ? "#2f2f2f" : "#CCCCCC",
borderRadius: 10,
loaderColor: themeMode === "dark" ? "#1a1a1a" : "#E0E0E0",
loaderColor: resolvedMode === "dark" ? "#1a1a1a" : "#E0E0E0",
jsonIconStyle: "round",
jsonTheme: themeMode === "dark" ? "summerfruit" : {
jsonTheme: resolvedMode === "dark" ? "summerfruit" : {
base00: "#ffffff", // background
base01: "#f0f0f0", // very light grey
base02: "#f5f5f5", // light grey
@@ -215,11 +225,11 @@ export const getTheme = (themeMode, brandColor) =>
},
jsonCollapseStringsAfterLength: 100,
drawer: {
backgroundColor: themeMode === "dark" ? "#262626" : "#f9f9f9"
backgroundColor: resolvedMode === "dark" ? "#262626" : "#f9f9f9"
},
actionSidebarField: {
backgroundColor: themeMode === "dark" ? "#2F2F2F" : "#F1F1F1",
color: themeMode === "dark" ? "#ffffff" : "#000000",
backgroundColor: resolvedMode === "dark" ? "#2F2F2F" : "#F1F1F1",
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
borderRadius: 8,
height: 40,
border: "none",
@@ -228,53 +238,53 @@ export const getTheme = (themeMode, brandColor) =>
padding: 5,
width: "98%",
borderRadius: 5,
border: themeMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)",
backgroundColor: themeMode === "dark"
border: resolvedMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)",
backgroundColor: resolvedMode === "dark"
? "#1A1A1A"
: "#f1f1f1",
color: themeMode === "dark"
color: resolvedMode === "dark"
? "#F1F1F1"
: "#1A1A1A",
overflowX: "auto",
},
textFieldStyle: {
backgroundColor: themeMode === "dark" ? "#212121" : "#FFFFFF",
color: themeMode === "dark" ? "#ffffff" : "#000000",
backgroundColor: resolvedMode === "dark" ? "#212121" : "#FFFFFF",
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
borderRadius: "5px",
height: 40,
border: themeMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0",
border: resolvedMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0",
},
DialogStyle: {
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
backgroundColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
borderRadius: 2,
boxShadow: themeMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)",
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
boxShadow: resolvedMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)",
border: resolvedMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
},
innerTextfieldStyle: {
height: 40,
fontSize: 16,
backgroundColor: themeMode === "dark" ? "#212121" : "#f5f5f5",
backgroundColor: resolvedMode === "dark" ? "#212121" : "#f5f5f5",
},
tooltip: {
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
color: themeMode === "dark" ? "#ffffff" : "#000000",
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
backgroundColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
border: resolvedMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
},
chipStyle: {
backgroundColor: themeMode === "dark" ? "#333333" : "#F5F5F5",
borderColor: themeMode === "dark" ? "#444444" : "#E0E0E0",
color: themeMode === "dark" ? "#FFFFFF" : "#333333",
backgroundColor: resolvedMode === "dark" ? "#333333" : "#F5F5F5",
borderColor: resolvedMode === "dark" ? "#444444" : "#E0E0E0",
color: resolvedMode === "dark" ? "#FFFFFF" : "#333333",
},
defaultImage: "/images/no_image.png",
singulOrange: "/images/singul_orange.png",
singulGreen: "/images/singul_green.png",
singulBlackWhite: "/icons/workflow-page/shuffle_agent.png",
scrollbarColor: themeMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1",
scrollbarColorTransparent: themeMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent",
scrollbarColor: resolvedMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1",
scrollbarColorTransparent: resolvedMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent",
},
typography: {
fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`,
color: themeMode === "dark" ? "#ffffff" : "#000000",
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
useNextVariants: true,
fontWeightLight: 300,
fontWeightRegular: 400,
@@ -282,36 +292,36 @@ export const getTheme = (themeMode, brandColor) =>
fontWeightSemiBold: 600,
fontWeightBold: 700,
allVariants: {
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
},
h1: {
fontSize: 40,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h2: {
fontSize: 36,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h3: {
fontSize: 32,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h4: {
fontSize: 30,
fontWeight: 500,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
h6: {
fontSize: 22,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
body1: {
fontSize: 16,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
body2: {
fontSize: 14,
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
},
},
components: {
@@ -326,7 +336,7 @@ export const getTheme = (themeMode, brandColor) =>
{
props: { variant: 'text', color: 'primary' },
style: {
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
whiteSpace: "nowrap",
textWrap: "normal",
},
@@ -334,7 +344,7 @@ export const getTheme = (themeMode, brandColor) =>
{
props: { variant: 'text', color: 'secondary' },
style: {
color: themeMode === "dark" ? "#9E9E9E" : "#616161",
color: resolvedMode === "dark" ? "#9E9E9E" : "#616161",
whiteSpace: "nowrap",
textWrap: "normal",
},
@@ -342,24 +352,24 @@ export const getTheme = (themeMode, brandColor) =>
{
props: { variant: 'contained', color: 'primary' },
style: {
backgroundColor: themeMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35',
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
backgroundColor: resolvedMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35',
color: resolvedMode === "dark" ? '#1a1a1a': '#FFFFFF',
borderRadius: '4px',
whiteSpace: "nowrap",
textWrap: "normal",
transition: 'background-color 0.2s ease-in-out',
'&:hover': {
fontWeight: 600,
backgroundColor: themeMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F',
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
backgroundColor: resolvedMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F',
color: resolvedMode === "dark" ? '#1a1a1a': '#FFFFFF',
},
},
},
{
props: { variant: 'contained', color: 'secondary' },
style: {
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
backgroundColor: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
color: resolvedMode === "dark" ? '#ffffff' : '#4C4C4C',
borderRadius: '4px',
boxShadow: 'none',
whiteSpace: "nowrap",
@@ -367,23 +377,23 @@ export const getTheme = (themeMode, brandColor) =>
textWrap: "normal",
'&:hover': {
fontWeight: 600,
border: themeMode === "dark" ? '1px solid #f1f1f1' : 'none',
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
border: resolvedMode === "dark" ? '1px solid #f1f1f1' : 'none',
backgroundColor: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
color: resolvedMode === "dark" ? '#ffffff' : '#4C4C4C',
},
},
},
{
props: { variant: 'outlined', color: 'primary' },
style: {
borderColor: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
color: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
borderColor: resolvedMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
color: resolvedMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
whiteSpace: "nowrap",
fontWeight: 'normal',
textWrap: "normal",
'&:hover': {
backgroundColor: themeMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc",
color: themeMode === "dark" ? "#1a1a1a" : "#8a3d00",
backgroundColor: resolvedMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc",
color: resolvedMode === "dark" ? "#1a1a1a" : "#8a3d00",
fontWeight: 600,
},
},
@@ -392,14 +402,14 @@ export const getTheme = (themeMode, brandColor) =>
props: { variant: 'outlined', color: 'secondary' },
style: {
border: '1px solid #C5C5C5',
color: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
color: resolvedMode === "dark" ? '#C5C5C5' : '#2D2D2D',
whiteSpace: "nowrap",
textWrap: "normal",
'&:hover': {
backgroundColor: themeMode === "dark" ? '#C5C5C5' : '#EFEFEF',
borderColor: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
backgroundColor: resolvedMode === "dark" ? '#C5C5C5' : '#EFEFEF',
borderColor: resolvedMode === "dark" ? '#C5C5C5' : '#2D2D2D',
fontWeight: 600,
color: themeMode === "dark" ? '#1a1a1a' : '#1A1A1A',
color: resolvedMode === "dark" ? '#1a1a1a' : '#1A1A1A',
},
},
},
@@ -422,8 +432,8 @@ export const getTheme = (themeMode, brandColor) =>
background: 'linear-gradient(90deg, #e6743a 0%, #d4456e 50%, #8a4de8 100%)',
},
'&:disabled': {
background: themeMode === "dark" ? '#494949' : '#C9C9C9',
color: themeMode === "dark" ? '#9E9E9E' : '#616161',
background: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
color: resolvedMode === "dark" ? '#9E9E9E' : '#616161',
},
},
},
@@ -468,9 +478,9 @@ export const getTheme = (themeMode, brandColor) =>
},
'&:disabled': {
background: 'transparent',
color: themeMode === "dark" ? '#9E9E9E' : '#616161',
color: resolvedMode === "dark" ? '#9E9E9E' : '#616161',
'&::before': {
background: themeMode === "dark" ? '#494949' : '#C9C9C9',
background: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
},
},
},
@@ -480,7 +490,7 @@ export const getTheme = (themeMode, brandColor) =>
MuiTab: {
styleOverrides: {
root: {
color: themeMode === "dark" ? "#C5C5C5" : "#1A1A1A",
color: resolvedMode === "dark" ? "#C5C5C5" : "#1A1A1A",
},
},
},
@@ -489,7 +499,7 @@ export const getTheme = (themeMode, brandColor) =>
overrides: {
MuiMenu: {
list: {
backgroundColor: themeMode === "dark" ? "#27292d" : "#ffffff",
backgroundColor: resolvedMode === "dark" ? "#27292d" : "#ffffff",
},
},
MuiCssBaseline: {
@@ -535,4 +545,5 @@ export const getTheme = (themeMode, brandColor) =>
},
},
});
}
-1
View File
@@ -65,7 +65,6 @@ import { Context } from "../context/ContextApi.jsx";
import {
SearchBox,
StaticRefinementList,
RefinementList,
InstantSearch,
connectSearchBox,
+50 -17
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,15 +1539,32 @@ const Docs = (defaultprops) => {
}
</div>
<div style={IndexBar}>
{userdata?.support && isArticlePage && (
<Button
variant="contained"
color="secondary"
onClick={handleResetCache}
style={{marginBottom: "15px"}}
{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
>
Reset Cache
</Button>
<Button
variant="contained"
color="secondary"
onClick={handleResetCache}
style={{marginBottom: "15px"}}
>
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",