diff --git a/packages/ui/src/assets/images/langchain.png b/packages/ui/src/assets/images/langchain.png index 587dd3140..d8ee337ff 100644 Binary files a/packages/ui/src/assets/images/langchain.png and b/packages/ui/src/assets/images/langchain.png differ 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 + + ) }