From 85df44bca4f5f6cde83098c41bf0afa209e3ef9a Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Thu, 16 Apr 2026 14:57:43 +0100 Subject: [PATCH] Feat/FlowConfigDialog UI Redesign (#6229) * enhance ChatflowConfigurationDialog with new configuration sections and improved UI * fix: remove background color override for MuiSvgIcon in dark mode --- packages/ui/src/assets/images/langchain.png | Bin 58913 -> 5357 bytes packages/ui/src/themes/compStyleOverride.js | 3 +- .../dialog/ChatflowConfigurationDialog.jsx | 556 ++++++++++++++---- .../ui-component/extended/AllowedDomains.jsx | 31 +- .../src/ui-component/extended/AnalyseFlow.jsx | 17 +- .../ui-component/extended/ChatFeedback.jsx | 16 +- .../src/ui-component/extended/FileUpload.jsx | 149 ++--- .../ui-component/extended/FollowUpPrompts.jsx | 8 +- .../ui/src/ui-component/extended/Leads.jsx | 18 +- .../src/ui-component/extended/McpServer.jsx | 32 +- .../ui-component/extended/OverrideConfig.jsx | 120 +++- .../ui-component/extended/PostProcessing.jsx | 93 ++- .../src/ui-component/extended/RateLimit.jsx | 55 +- .../ui-component/extended/SpeechToText.jsx | 18 +- .../ui-component/extended/StarterPrompts.jsx | 44 +- .../ui-component/extended/TextToSpeech.jsx | 18 +- 16 files changed, 827 insertions(+), 351 deletions(-) diff --git a/packages/ui/src/assets/images/langchain.png b/packages/ui/src/assets/images/langchain.png index 587dd314071048b3b8d892e0ba7f409c12da2712..d8ee337ff1fd51121412cf7a67f772e1f669167f 100644 GIT binary patch literal 5357 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 literal 58913 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 @Bok*SLeD@x@yGivkx9M1+2R)2) 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°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 S_sACEb*U(5$k)o6h5Qczr~XI) diff --git a/packages/ui/src/themes/compStyleOverride.js b/packages/ui/src/themes/compStyleOverride.js index c04cc3f17..18014c705 100644 --- a/packages/ui/src/themes/compStyleOverride.js +++ b/packages/ui/src/themes/compStyleOverride.js @@ -45,8 +45,7 @@ export default function componentStyleOverrides(theme) { MuiSvgIcon: { styleOverrides: { root: { - color: theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit', - background: theme?.customization?.isDarkMode ? theme.colors?.darkPrimaryLight : 'inherit' + color: theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit' } } }, diff --git a/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx b/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx index f657b96ad..c5b117c11 100644 --- a/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx +++ b/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx @@ -1,11 +1,33 @@ import PropTypes from 'prop-types' -import { useState } from 'react' +import { useState, useMemo } from 'react' import { createPortal } from 'react-dom' -import { Box, Dialog, DialogContent, DialogTitle, Tabs, Tab } from '@mui/material' -import { tabsClasses } from '@mui/material/Tabs' +import { useSelector } from 'react-redux' +import { Box, Dialog, DialogContent, DialogTitle, Typography, IconButton } from '@mui/material' +import { useTheme } from '@mui/material/styles' +import { + IconX, + IconShieldLock, + IconWorldWww, + IconUserPlus, + IconMessageChatbot, + IconArrowForwardUp, + IconThumbUp, + IconMicrophone, + IconVolume, + IconUpload, + IconChartBar, + IconCode, + IconServer, + IconAdjustments +} from '@tabler/icons-react' +import PerfectScrollbar from 'react-perfect-scrollbar' + +// Section components import SpeechToText from '@/ui-component/extended/SpeechToText' import TextToSpeech from '@/ui-component/extended/TextToSpeech' -import Security from '@/ui-component/extended/Security' +import RateLimit from '@/ui-component/extended/RateLimit' +import AllowedDomains from '@/ui-component/extended/AllowedDomains' +import OverrideConfig from '@/ui-component/extended/OverrideConfig' import ChatFeedback from '@/ui-component/extended/ChatFeedback' import AnalyseFlow from '@/ui-component/extended/AnalyseFlow' import StarterPrompts from '@/ui-component/extended/StarterPrompts' @@ -15,146 +37,448 @@ import FileUpload from '@/ui-component/extended/FileUpload' import PostProcessing from '@/ui-component/extended/PostProcessing' import McpServer from '@/ui-component/extended/McpServer' -const CHATFLOW_CONFIGURATION_TABS = [ +const CONFIGURATION_GROUPS = [ { - label: 'Security', - id: 'security' + label: 'General', + sections: [ + { + label: 'Rate Limit', + id: 'rateLimit', + icon: IconShieldLock, + description: 'Limit API requests per time window' + }, + { + label: 'Allowed Domains', + id: 'allowedDomains', + icon: IconWorldWww, + description: 'Restrict chatbot to specific domains' + }, + { + label: 'Leads', + id: 'leads', + icon: IconUserPlus, + description: 'Capture visitor contact information' + } + ] }, { - label: 'Starter Prompts', - id: 'conversationStarters' + label: 'Chat', + sections: [ + { + label: 'Starter Prompts', + id: 'conversationStarters', + icon: IconMessageChatbot, + description: 'Suggested prompts for new conversations' + }, + { + label: 'Follow-up Prompts', + id: 'followUpPrompts', + icon: IconArrowForwardUp, + description: 'Auto-generate follow-up questions' + }, + { + label: 'Chat Feedback', + id: 'chatFeedback', + icon: IconThumbUp, + description: 'Allow users to rate responses' + } + ] }, { - label: 'Follow-up Prompts', - id: 'followUpPrompts' + label: 'Media & Files', + sections: [ + { + label: 'Speech to Text', + id: 'speechToText', + icon: IconMicrophone, + description: 'Voice input transcription' + }, + { + label: 'Text to Speech', + id: 'textToSpeech', + icon: IconVolume, + description: 'Audio response playback' + }, + { + label: 'File Upload', + id: 'fileUpload', + icon: IconUpload, + description: 'Allow file uploads in chat' + } + ] }, { - label: 'Speech to Text', - id: 'speechToText' - }, - { - label: 'Text to Speech', - id: 'textToSpeech' - }, - { - label: 'Chat Feedback', - id: 'chatFeedback' - }, - { - label: 'Analyse Chatflow', - id: 'analyseChatflow' - }, - { - label: 'Leads', - id: 'leads' - }, - { - label: 'File Upload', - id: 'fileUpload' - }, - { - label: 'Post Processing', - id: 'postProcessing' - }, - { - label: 'MCP Server', - id: 'mcpServer' + label: 'Advanced', + sections: [ + { + label: 'Analytics', + id: 'analyseChatflow', + icon: IconChartBar, + description: 'Connect analytics providers' + }, + { + label: 'Post Processing', + id: 'postProcessing', + icon: IconCode, + description: 'Custom JavaScript post-processing' + }, + { + label: 'MCP Server', + id: 'mcpServer', + icon: IconServer, + description: 'Model Context Protocol server' + }, + { + label: 'Override Config', + id: 'overrideConfig', + icon: IconAdjustments, + description: 'Override flow configuration via API' + } + ] } ] -function TabPanel(props) { - const { children, value, index, ...other } = props - return ( - - ) -} +function getSectionStatus(sectionId, chatflow) { + if (!chatflow) return false -TabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.number.isRequired, - value: PropTypes.number.isRequired -} + let chatbotConfig = {} + let apiConfig = {} + try { + chatbotConfig = chatflow.chatbotConfig ? JSON.parse(chatflow.chatbotConfig) : {} + apiConfig = chatflow.apiConfig ? JSON.parse(chatflow.apiConfig) : {} + } catch { + return false + } -function a11yProps(index) { - return { - id: `chatflow-config-tab-${index}`, - 'aria-controls': `chatflow-config-tabpanel-${index}` + switch (sectionId) { + case 'rateLimit': + return apiConfig?.rateLimit?.status === true + case 'allowedDomains': + return Array.isArray(chatbotConfig?.allowedOrigins) && chatbotConfig.allowedOrigins.some((o) => o && o.trim() !== '') + case 'leads': + return chatbotConfig?.leads?.status === true + case 'conversationStarters': { + const sp = chatbotConfig?.starterPrompts + if (!sp) return false + return Object.values(sp).some((entry) => entry?.prompt && entry.prompt.trim() !== '') + } + case 'followUpPrompts': + return chatbotConfig?.followUpPrompts?.status === true + case 'chatFeedback': + return chatbotConfig?.chatFeedback?.status === true + case 'speechToText': { + const stt = chatbotConfig?.speechToText + if (!stt) return false + return Object.values(stt).some((provider) => provider?.status === true) + } + case 'textToSpeech': { + const tts = chatbotConfig?.textToSpeech + if (!tts) return false + return Object.values(tts).some((provider) => provider?.status === true) + } + case 'fileUpload': + return chatbotConfig?.fullFileUpload?.status === true + case 'analyseChatflow': { + const ap = chatbotConfig?.analyticsProviders + if (!ap) return false + return Object.values(ap).some((provider) => provider?.status === true) + } + case 'postProcessing': + return chatbotConfig?.postProcessing?.status === true + case 'mcpServer': + return chatbotConfig?.mcpServer?.status === true + case 'overrideConfig': + return apiConfig?.overrideConfig?.status === true + default: + return false } } +// Flatten all sections for quick lookup +const ALL_SECTIONS = CONFIGURATION_GROUPS.flatMap((g) => g.sections) + +const SIDEBAR_WIDTH = 220 + const ChatflowConfigurationDialog = ({ show, isAgentCanvas, dialogProps, onCancel }) => { const portalElement = document.getElementById('portal') - const [tabValue, setTabValue] = useState(0) + const theme = useTheme() + const chatflow = useSelector((state) => state.canvas.chatflow) + const customization = useSelector((state) => state.customization) - const filteredTabs = CHATFLOW_CONFIGURATION_TABS.filter((tab) => !isAgentCanvas || !tab.hideInAgentFlow) + const [activeSection, setActiveSection] = useState('rateLimit') + const [mcpServerEnabled, setMcpServerEnabled] = useState(false) + + const isDark = theme.palette.mode === 'dark' || customization?.isDarkMode + + // Filter groups/sections based on agent canvas + const filteredGroups = useMemo(() => { + return CONFIGURATION_GROUPS.map((group) => ({ + ...group, + sections: group.sections.filter((section) => !isAgentCanvas || !section.hideInAgentFlow) + })).filter((group) => group.sections.length > 0) + }, [isAgentCanvas]) + + // Get all section IDs for validation + const allSectionIds = useMemo(() => { + return filteredGroups.flatMap((g) => g.sections.map((s) => s.id)) + }, [filteredGroups]) + + // Reset activeSection if current one is filtered out + const currentSection = allSectionIds.includes(activeSection) ? activeSection : allSectionIds[0] || 'rateLimit' + const currentSectionData = ALL_SECTIONS.find((s) => s.id === currentSection) + + const renderContent = () => { + const props = { dialogProps } + switch (currentSection) { + case 'rateLimit': + return + case 'allowedDomains': + return + case 'leads': + return + case 'conversationStarters': + return + case 'followUpPrompts': + return + case 'chatFeedback': + return + case 'speechToText': + return + case 'textToSpeech': + return + case 'fileUpload': + return + case 'analyseChatflow': + return + case 'postProcessing': + return + case 'mcpServer': + return setMcpServerEnabled(enabled)} /> + case 'overrideConfig': + return + default: + return null + } + } const component = show ? ( - - {dialogProps.title} - - - + {dialogProps.title} + setTabValue(value)} - aria-label='tabs' - variant='scrollable' - scrollButtons='auto' > - {filteredTabs.map((item, index) => ( - - ))} - - {filteredTabs.map((item, index) => ( - - {item.id === 'security' && } - {item.id === 'conversationStarters' ? : null} - {item.id === 'followUpPrompts' ? : null} - {item.id === 'speechToText' ? : null} - {item.id === 'textToSpeech' ? : null} - {item.id === 'chatFeedback' ? : null} - {item.id === 'analyseChatflow' ? : null} - {item.id === 'leads' ? : null} - {item.id === 'fileUpload' ? : null} - {item.id === 'postProcessing' ? : null} - {item.id === 'mcpServer' ? : null} - - ))} + + + + + {/* Body: Sidebar + Content */} + + {/* Sidebar */} + + + + {filteredGroups.map((group, groupIndex) => ( + + {/* Group label */} + 0 ? 2 : 0.5, + pb: 0.75 + }} + > + {group.label} + + + {/* Section items */} + {group.sections.map((section) => { + const isActive = currentSection === section.id + const isEnabled = + section.id === 'mcpServer' ? mcpServerEnabled : getSectionStatus(section.id, chatflow) + const SectionIcon = section.icon + + return ( + setActiveSection(section.id)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 1.25, + px: 1.5, + py: 0.875, + borderRadius: '8px', + cursor: 'pointer', + position: 'relative', + color: isActive ? 'primary.main' : isDark ? 'grey.300' : 'grey.700', + bgcolor: isActive + ? isDark + ? 'rgba(33, 150, 243, 0.12)' + : 'rgba(33, 150, 243, 0.06)' + : 'transparent', + transition: 'all 0.15s ease', + '&:hover': { + bgcolor: isActive + ? isDark + ? 'rgba(33, 150, 243, 0.16)' + : 'rgba(33, 150, 243, 0.08)' + : isDark + ? 'rgba(255,255,255,0.04)' + : 'rgba(0,0,0,0.03)' + }, + userSelect: 'none' + }} + > + {/* Icon */} + + + {/* Label */} + + {section.label} + + + {/* Status badge - only show when enabled */} + {isEnabled && ( + + ON + + )} + + ) + })} + + ))} + + + + + {/* Content area */} + + + + {/* Section header */} + + + {currentSectionData?.label || ''} + + {currentSectionData?.description && ( + + {currentSectionData.description} + + )} + + + {/* Section content */} + {renderContent()} + + + ) : null diff --git a/packages/ui/src/ui-component/extended/AllowedDomains.jsx b/packages/ui/src/ui-component/extended/AllowedDomains.jsx index 75ae32a63..29a44a579 100644 --- a/packages/ui/src/ui-component/extended/AllowedDomains.jsx +++ b/packages/ui/src/ui-component/extended/AllowedDomains.jsx @@ -17,7 +17,7 @@ import useNotifier from '@/utils/useNotifier' // API import chatflowsApi from '@/api/chatflows' -const AllowedDomains = ({ dialogProps, onConfirm }) => { +const AllowedDomains = ({ dialogProps, onConfirm, hideTitle = false }) => { const dispatch = useDispatch() useNotifier() @@ -119,14 +119,16 @@ const AllowedDomains = ({ dialogProps, onConfirm }) => { }, [dialogProps]) return ( - - - Allowed Domains - - + + {!hideTitle && ( + + Allowed Domains + + + )} Domains @@ -193,16 +195,19 @@ const AllowedDomains = ({ dialogProps, onConfirm }) => { /> - - Save - + + + Save + + ) } AllowedDomains.propTypes = { dialogProps: PropTypes.object, - onConfirm: PropTypes.func + onConfirm: PropTypes.func, + hideTitle: PropTypes.bool } export default AllowedDomains diff --git a/packages/ui/src/ui-component/extended/AnalyseFlow.jsx b/packages/ui/src/ui-component/extended/AnalyseFlow.jsx index de162e51a..84038f4c6 100644 --- a/packages/ui/src/ui-component/extended/AnalyseFlow.jsx +++ b/packages/ui/src/ui-component/extended/AnalyseFlow.jsx @@ -15,6 +15,7 @@ import { ListItemAvatar, ListItemText } from '@mui/material' +import { useTheme } from '@mui/material/styles' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import { IconX } from '@tabler/icons-react' @@ -221,6 +222,7 @@ const analyticProviders = [ const AnalyseFlow = ({ dialogProps }) => { const dispatch = useDispatch() + const theme = useTheme() useNotifier() @@ -339,7 +341,12 @@ const AnalyseFlow = ({ dialogProps }) => { sx={{ ml: 1 }} primary={provider.label} secondary={ - + {provider.url} } @@ -416,9 +423,11 @@ const AnalyseFlow = ({ dialogProps }) => { ))} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/ChatFeedback.jsx b/packages/ui/src/ui-component/extended/ChatFeedback.jsx index 132d073e2..ca55dc5b7 100644 --- a/packages/ui/src/ui-component/extended/ChatFeedback.jsx +++ b/packages/ui/src/ui-component/extended/ChatFeedback.jsx @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react' import PropTypes from 'prop-types' // material-ui -import { Button, Box } from '@mui/material' +import { Button, Box, Stack } from '@mui/material' import { IconX } from '@tabler/icons-react' // Project import @@ -91,14 +91,14 @@ const ChatFeedback = ({ dialogProps, onConfirm }) => { }, [dialogProps]) return ( - <> - - + + + + + Save + - - Save - - + ) } diff --git a/packages/ui/src/ui-component/extended/FileUpload.jsx b/packages/ui/src/ui-component/extended/FileUpload.jsx index d688cb754..fc5613590 100644 --- a/packages/ui/src/ui-component/extended/FileUpload.jsx +++ b/packages/ui/src/ui-component/extended/FileUpload.jsx @@ -5,7 +5,19 @@ import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackba import parser from 'html-react-parser' // material-ui -import { Button, Box, Typography, FormControl, RadioGroup, FormControlLabel, Radio } from '@mui/material' +import { + Button, + Box, + Typography, + FormControl, + RadioGroup, + FormControlLabel, + Radio, + Accordion, + AccordionSummary, + AccordionDetails +} from '@mui/material' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import { IconX, IconBulb } from '@tabler/icons-react' // Project import @@ -20,7 +32,7 @@ import chatflowsApi from '@/api/chatflows' const message = `The full contents of uploaded files will be converted to text and sent to the Agent.
-Refer docs for more details.` +Refer docs for more details.` const availableFileTypes = [ { name: 'CSS', ext: 'text/css', extension: '.css' }, @@ -52,8 +64,6 @@ const FileUpload = ({ dialogProps }) => { const [allowedFileTypes, setAllowedFileTypes] = useState([]) const [chatbotConfig, setChatbotConfig] = useState({}) const [pdfUsage, setPdfUsage] = useState('perPage') - const [pdfLegacyBuild, setPdfLegacyBuild] = useState(false) - const handleChange = (value) => { setFullFileUpload(value) } @@ -71,18 +81,13 @@ const FileUpload = ({ dialogProps }) => { setPdfUsage(event.target.value) } - const handleLegacyBuildChange = (value) => { - setPdfLegacyBuild(value) - } - const onSave = async () => { try { const value = { status: fullFileUpload, allowedUploadFileTypes: allowedFileTypes.join(','), pdfFile: { - usage: pdfUsage, - legacyBuild: pdfLegacyBuild + usage: pdfUsage } } chatbotConfig.fullFileUpload = value @@ -140,13 +145,8 @@ const FileUpload = ({ dialogProps }) => { const allowedFileTypes = chatbotConfig.fullFileUpload.allowedUploadFileTypes.split(',') setAllowedFileTypes(allowedFileTypes) } - if (chatbotConfig.fullFileUpload?.pdfFile) { - if (chatbotConfig.fullFileUpload.pdfFile.usage) { - setPdfUsage(chatbotConfig.fullFileUpload.pdfFile.usage) - } - if (chatbotConfig.fullFileUpload.pdfFile.legacyBuild !== undefined) { - setPdfLegacyBuild(chatbotConfig.fullFileUpload.pdfFile.legacyBuild) - } + if (chatbotConfig.fullFileUpload?.pdfFile?.usage) { + setPdfUsage(chatbotConfig.fullFileUpload.pdfFile.usage) } } catch (e) { setChatbotConfig({}) @@ -170,31 +170,27 @@ const FileUpload = ({ dialogProps }) => { mb: 2 }} > -
-
- - {parser(message)} -
-
+ + {parser(message)} +
- Allow Uploads of Type + Allow Uploads of Type
{ disabled={!fullFileUpload} onChange={handleAllowedFileTypesChange} /> -
))} - {allowedFileTypes.includes('application/pdf') && fullFileUpload && ( - - } + sx={{ minHeight: 40, px: 2, '& .MuiAccordionSummary-content': { my: 0.75 } }} > - PDF Configuration - - - - PDF Usage - - - } label='One document per page' /> - } label='One document per file' /> - - - - - - - - + Advanced Settings + + + {/* PDF Processing */} + {allowedFileTypes.includes('application/pdf') && ( + + + PDF Processing + + + + } + label={One document per page} + /> + } + label={One document per file} + /> + + + + )} + + )} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/FollowUpPrompts.jsx b/packages/ui/src/ui-component/extended/FollowUpPrompts.jsx index bc0d95bc1..8a7a711b8 100644 --- a/packages/ui/src/ui-component/extended/FollowUpPrompts.jsx +++ b/packages/ui/src/ui-component/extended/FollowUpPrompts.jsx @@ -601,9 +601,11 @@ const FollowUpPrompts = ({ dialogProps }) => { )} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/Leads.jsx b/packages/ui/src/ui-component/extended/Leads.jsx index 395902986..58fa8169b 100644 --- a/packages/ui/src/ui-component/extended/Leads.jsx +++ b/packages/ui/src/ui-component/extended/Leads.jsx @@ -160,14 +160,16 @@ const Leads = ({ dialogProps }) => { )} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/McpServer.jsx b/packages/ui/src/ui-component/extended/McpServer.jsx index 892c0dba8..b712130a9 100644 --- a/packages/ui/src/ui-component/extended/McpServer.jsx +++ b/packages/ui/src/ui-component/extended/McpServer.jsx @@ -23,7 +23,7 @@ import useNotifier from '@/utils/useNotifier' import mcpServerApi from '@/api/mcpserver' import chatflowsApi from '@/api/chatflows' -const McpServer = ({ dialogProps }) => { +const McpServer = ({ dialogProps, onStatusChange }) => { const dispatch = useDispatch() const theme = useTheme() const customization = useSelector((state) => state.customization) @@ -123,6 +123,7 @@ const McpServer = ({ dialogProps }) => { setToken(resp.data.token || '') setToolName(resp.data.toolName || '') setDescription(resp.data.description || '') + onStatusChange?.(resp.data.enabled) showSuccess('MCP Server settings saved') } } else { @@ -136,12 +137,14 @@ const McpServer = ({ dialogProps }) => { setToolName(resp.data.toolName || '') setDescription(resp.data.description || '') setHasExistingConfig(true) + onStatusChange?.(resp.data.enabled) showSuccess('MCP Server settings saved') } } } else { await mcpServerApi.deleteMcpServerConfig(dialogProps.chatflow.id) setMcpEnabled(false) + onStatusChange?.(false) showSuccess('MCP Server disabled') } await refreshChatflowStore() @@ -215,13 +218,15 @@ const McpServer = ({ dialogProps }) => { useEffect(() => { if (getMcpServerConfigApi.data) { - setMcpEnabled(getMcpServerConfigApi.data.enabled || false) + const enabled = getMcpServerConfigApi.data.enabled || false + setMcpEnabled(enabled) setToolName(getMcpServerConfigApi.data.toolName || '') setDescription(getMcpServerConfigApi.data.description || '') setToken(getMcpServerConfigApi.data.token || '') setHasExistingConfig(!!getMcpServerConfigApi.data.token) + onStatusChange?.(enabled) } - }, [getMcpServerConfigApi.data]) + }, [getMcpServerConfigApi.data]) // eslint-disable-line react-hooks/exhaustive-deps if (getMcpServerConfigApi.loading) { return ( @@ -391,20 +396,23 @@ const McpServer = ({ dialogProps }) => { )} - - {loading ? 'Saving...' : 'Save'} - + + + {loading ? 'Saving...' : 'Save'} + + ) } McpServer.propTypes = { - dialogProps: PropTypes.object + dialogProps: PropTypes.object, + onStatusChange: PropTypes.func } export default McpServer diff --git a/packages/ui/src/ui-component/extended/OverrideConfig.jsx b/packages/ui/src/ui-component/extended/OverrideConfig.jsx index 32397b4b9..0ca1b4c85 100644 --- a/packages/ui/src/ui-component/extended/OverrideConfig.jsx +++ b/packages/ui/src/ui-component/extended/OverrideConfig.jsx @@ -5,6 +5,7 @@ import { Accordion, AccordionDetails, AccordionSummary, + Box, Button, Paper, Stack, @@ -17,7 +18,6 @@ import { Typography, Card } from '@mui/material' -import { useTheme } from '@mui/material/styles' // Project import import { StyledButton } from '@/ui-component/button/StyledButton' @@ -39,6 +39,9 @@ import variablesApi from '@/api/variables' // utils const OverrideConfigTable = ({ columns, onToggle, rows, sx }) => { + const customization = useSelector((state) => state.customization) + const isDark = customization?.isDarkMode + const handleChange = (enabled, row) => { onToggle(row, enabled) } @@ -47,10 +50,8 @@ const OverrideConfigTable = ({ columns, onToggle, rows, sx }) => { if (key === 'enabled') { return handleChange(enabled, row)} value={row.enabled} /> } else if (key === 'type' && row.schema) { - // If there's schema information, add a tooltip let schemaContent if (Array.isArray(row.schema)) { - // Handle array format: [{ name: "field", type: "string" }, ...] schemaContent = '[
' + row.schema @@ -67,30 +68,53 @@ const OverrideConfigTable = ({ columns, onToggle, rows, sx }) => { .join(',
') + '
]' } else if (typeof row.schema === 'object' && row.schema !== null) { - // Handle object format: { "field": "string", "field2": "number", ... } schemaContent = JSON.stringify(row.schema, null, 2).replace(/\n/g, '
').replace(/ /g, ' ') } else { schemaContent = 'No schema available' } return ( - - {row[key]} + + {row[key]} Schema:
${schemaContent}`} />
) } else { - return row[key] + return {row[key]} } } + const columnLabels = { label: 'Label', name: 'Name', type: 'Type', enabled: 'On' } + return ( - - + +
{columns.map((col, index) => ( - {col.charAt(0).toUpperCase() + col.slice(1)} + + {columnLabels[col] || col.charAt(0).toUpperCase() + col.slice(1)} + ))} @@ -99,7 +123,19 @@ const OverrideConfigTable = ({ columns, onToggle, rows, sx }) => { {Object.keys(row).map((key, index) => { if (key !== 'id' && key !== 'schema') { - return {renderCellContent(key, row)} + return ( + + {renderCellContent(key, row)} + + ) } })} @@ -117,14 +153,14 @@ OverrideConfigTable.propTypes = { onToggle: PropTypes.func } -const OverrideConfig = ({ dialogProps }) => { +const OverrideConfig = ({ dialogProps, hideTitle = false }) => { const dispatch = useDispatch() + const customization = useSelector((state) => state.customization) const chatflow = useSelector((state) => state.canvas.chatflow) const chatflowid = chatflow.id const apiConfig = chatflow.apiConfig ? JSON.parse(chatflow.apiConfig) : {} useNotifier() - const theme = useTheme() const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) @@ -359,22 +395,32 @@ const OverrideConfig = ({ dialogProps }) => { }, [getAllVariablesApi.data]) return ( - - - Override Configuration - documentation for more information.' - } - /> - + + {!hideTitle && ( + + Override Configuration + documentation for more information.' + } + /> + + )} {overrideConfigStatus && ( <> {nodeOverrides && nodeConfig && ( - + Nodes @@ -388,6 +434,11 @@ const OverrideConfig = ({ dialogProps }) => { onChange={handleAccordionChange(nodeLabel)} key={nodeLabel} disableGutters + sx={{ + '&:before': { + bgcolor: customization.isDarkMode ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.06)' + } + }} > } @@ -444,7 +495,15 @@ const OverrideConfig = ({ dialogProps }) => { )} {variableOverrides && variableOverrides.length > 0 && ( - + Variables @@ -459,15 +518,18 @@ const OverrideConfig = ({ dialogProps }) => { )} - - Save - + + + Save + + ) } OverrideConfig.propTypes = { - dialogProps: PropTypes.object + dialogProps: PropTypes.object, + hideTitle: PropTypes.bool } export default OverrideConfig diff --git a/packages/ui/src/ui-component/extended/PostProcessing.jsx b/packages/ui/src/ui-component/extended/PostProcessing.jsx index fb9543a55..e6cab9400 100644 --- a/packages/ui/src/ui-component/extended/PostProcessing.jsx +++ b/packages/ui/src/ui-component/extended/PostProcessing.jsx @@ -179,7 +179,7 @@ const PostProcessing = ({ dialogProps }) => { style={{ marginTop: '10px', border: '1px solid', - borderColor: theme.palette.grey['300'], + borderColor: customization.isDarkMode ? 'rgba(255,255,255,0.12)' : theme.palette.grey['300'], borderRadius: '6px', height: '200px', width: '100%' @@ -196,7 +196,16 @@ const PostProcessing = ({ dialogProps }) => { /> - + { }} > }> - Available Variables + Available Variables - -
+ +
- Variable - Type - Description + + Variable + + + Type + + + Description + - + $flow.rawOutput @@ -283,11 +334,11 @@ const PostProcessing = ({ dialogProps }) => { List of artifacts generated during execution - + $flow.fileAnnotations - array - File annotations associated with the response + array + File annotations associated with the response
@@ -295,14 +346,16 @@ const PostProcessing = ({ dialogProps }) => { - - Save - + + + Save + + { +const RateLimit = ({ dialogProps, hideTitle = false }) => { const dispatch = useDispatch() const chatflow = useSelector((state) => state.canvas.chatflow) const chatflowid = chatflow.id @@ -147,36 +147,39 @@ const RateLimit = ({ dialogProps }) => { } return ( - - - Rate Limit{' '} - Rate Limit Setup Guide to set up Rate Limit correctly in your hosting environment.' - } - /> - - - - {rateLimitStatus && ( - - {textField(limitMax, 'limitMax', 'Message Limit per Duration', 'number', '5')} - {textField(limitDuration, 'limitDuration', 'Duration in Second', 'number', '60')} - {textField(limitMsg, 'limitMsg', 'Limit Message', 'string', 'You have reached the quota')} - - )} - - onSave()} sx={{ width: 'auto' }}> - Save - + + {!hideTitle && ( + + Rate Limit{' '} + Rate Limit Setup Guide to set up Rate Limit correctly in your hosting environment.' + } + /> + + )} + + {rateLimitStatus && ( + + {textField(limitMax, 'limitMax', 'Message Limit per Duration', 'number', '5')} + {textField(limitDuration, 'limitDuration', 'Duration in Second', 'number', '60')} + {textField(limitMsg, 'limitMsg', 'Limit Message', 'string', 'You have reached the quota')} + + )} + + onSave()} sx={{ minWidth: 100 }}> + Save + + ) } RateLimit.propTypes = { isSessionMemory: PropTypes.bool, - dialogProps: PropTypes.object + dialogProps: PropTypes.object, + hideTitle: PropTypes.bool } export default RateLimit diff --git a/packages/ui/src/ui-component/extended/SpeechToText.jsx b/packages/ui/src/ui-component/extended/SpeechToText.jsx index 2c732b2d0..2ca7fd95c 100644 --- a/packages/ui/src/ui-component/extended/SpeechToText.jsx +++ b/packages/ui/src/ui-component/extended/SpeechToText.jsx @@ -478,14 +478,16 @@ const SpeechToText = ({ dialogProps, onConfirm }) => { ))} )} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/StarterPrompts.jsx b/packages/ui/src/ui-component/extended/StarterPrompts.jsx index 31504fc59..861bea1e1 100644 --- a/packages/ui/src/ui-component/extended/StarterPrompts.jsx +++ b/packages/ui/src/ui-component/extended/StarterPrompts.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions' // material-ui -import { Button, IconButton, OutlinedInput, Box, List, InputAdornment } from '@mui/material' +import { Button, IconButton, OutlinedInput, Box, List, InputAdornment, Typography } from '@mui/material' import { IconX, IconTrash, IconPlus, IconBulb } from '@tabler/icons-react' // Project import @@ -133,28 +133,24 @@ const StarterPrompts = ({ dialogProps, onConfirm }) => { return ( <> -
-
- - - Starter prompts will only be shown when there is no messages on the chat - -
-
+ + + Starter prompts will only be shown when there are no messages on the chat + + :not(style)': { m: 1 }, pt: 2 }}> {inputFields.map((data, index) => { @@ -199,9 +195,11 @@ const StarterPrompts = ({ dialogProps, onConfirm }) => { })} - - Save - + + + Save + + ) } diff --git a/packages/ui/src/ui-component/extended/TextToSpeech.jsx b/packages/ui/src/ui-component/extended/TextToSpeech.jsx index 226d45a1e..8ce171615 100644 --- a/packages/ui/src/ui-component/extended/TextToSpeech.jsx +++ b/packages/ui/src/ui-component/extended/TextToSpeech.jsx @@ -641,14 +641,16 @@ const TextToSpeech = ({ dialogProps }) => { )} - - Save - + + + Save + + ) }