|
| 1 | +import { useDispatch } from 'react-redux' |
| 2 | +import { useState, useEffect } from 'react' |
| 3 | +import PropTypes from 'prop-types' |
| 4 | +import { useSelector } from 'react-redux' |
| 5 | + |
| 6 | +// material-ui |
| 7 | +import { IconButton, Button, Box, Typography } from '@mui/material' |
| 8 | +import { IconArrowsMaximize, IconBulb, IconX } from '@tabler/icons-react' |
| 9 | +import { useTheme } from '@mui/material/styles' |
| 10 | + |
| 11 | +// Project import |
| 12 | +import { StyledButton } from '@/ui-component/button/StyledButton' |
| 13 | +import { SwitchInput } from '@/ui-component/switch/Switch' |
| 14 | +import { CodeEditor } from '@/ui-component/editor/CodeEditor' |
| 15 | +import ExpandTextDialog from '@/ui-component/dialog/ExpandTextDialog' |
| 16 | + |
| 17 | +// store |
| 18 | +import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions' |
| 19 | +import useNotifier from '@/utils/useNotifier' |
| 20 | + |
| 21 | +// API |
| 22 | +import chatflowsApi from '@/api/chatflows' |
| 23 | + |
| 24 | +const sampleFunction = `return $flow.rawOutput + " This is a post processed response!";` |
| 25 | + |
| 26 | +const PostProcessing = ({ dialogProps }) => { |
| 27 | + const dispatch = useDispatch() |
| 28 | + |
| 29 | + useNotifier() |
| 30 | + const theme = useTheme() |
| 31 | + const customization = useSelector((state) => state.customization) |
| 32 | + |
| 33 | + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) |
| 34 | + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) |
| 35 | + |
| 36 | + const [postProcessingEnabled, setPostProcessingEnabled] = useState(false) |
| 37 | + const [postProcessingFunction, setPostProcessingFunction] = useState('') |
| 38 | + const [chatbotConfig, setChatbotConfig] = useState({}) |
| 39 | + const [showExpandDialog, setShowExpandDialog] = useState(false) |
| 40 | + const [expandDialogProps, setExpandDialogProps] = useState({}) |
| 41 | + |
| 42 | + const handleChange = (value) => { |
| 43 | + setPostProcessingEnabled(value) |
| 44 | + } |
| 45 | + |
| 46 | + const onExpandDialogClicked = (value) => { |
| 47 | + const dialogProps = { |
| 48 | + value, |
| 49 | + inputParam: { |
| 50 | + label: 'Post Processing Function', |
| 51 | + name: 'postProcessingFunction', |
| 52 | + type: 'code', |
| 53 | + placeholder: sampleFunction, |
| 54 | + hideCodeExecute: true |
| 55 | + }, |
| 56 | + languageType: 'js', |
| 57 | + confirmButtonName: 'Save', |
| 58 | + cancelButtonName: 'Cancel' |
| 59 | + } |
| 60 | + setExpandDialogProps(dialogProps) |
| 61 | + setShowExpandDialog(true) |
| 62 | + } |
| 63 | + |
| 64 | + const onSave = async () => { |
| 65 | + try { |
| 66 | + let value = { |
| 67 | + postProcessing: { |
| 68 | + enabled: postProcessingEnabled, |
| 69 | + customFunction: JSON.stringify(postProcessingFunction) |
| 70 | + } |
| 71 | + } |
| 72 | + chatbotConfig.postProcessing = value.postProcessing |
| 73 | + const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, { |
| 74 | + chatbotConfig: JSON.stringify(chatbotConfig) |
| 75 | + }) |
| 76 | + if (saveResp.data) { |
| 77 | + enqueueSnackbar({ |
| 78 | + message: 'Post Processing Settings Saved', |
| 79 | + options: { |
| 80 | + key: new Date().getTime() + Math.random(), |
| 81 | + variant: 'success', |
| 82 | + action: (key) => ( |
| 83 | + <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}> |
| 84 | + <IconX /> |
| 85 | + </Button> |
| 86 | + ) |
| 87 | + } |
| 88 | + }) |
| 89 | + dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data }) |
| 90 | + } |
| 91 | + } catch (error) { |
| 92 | + enqueueSnackbar({ |
| 93 | + message: `Failed to save Post Processing Settings: ${ |
| 94 | + typeof error.response.data === 'object' ? error.response.data.message : error.response.data |
| 95 | + }`, |
| 96 | + options: { |
| 97 | + key: new Date().getTime() + Math.random(), |
| 98 | + variant: 'error', |
| 99 | + persist: true, |
| 100 | + action: (key) => ( |
| 101 | + <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}> |
| 102 | + <IconX /> |
| 103 | + </Button> |
| 104 | + ) |
| 105 | + } |
| 106 | + }) |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + useEffect(() => { |
| 111 | + if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) { |
| 112 | + let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig) |
| 113 | + setChatbotConfig(chatbotConfig || {}) |
| 114 | + if (chatbotConfig.postProcessing) { |
| 115 | + setPostProcessingEnabled(chatbotConfig.postProcessing.enabled) |
| 116 | + if (chatbotConfig.postProcessing.customFunction) { |
| 117 | + setPostProcessingFunction(JSON.parse(chatbotConfig.postProcessing.customFunction)) |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + return () => {} |
| 123 | + }, [dialogProps]) |
| 124 | + |
| 125 | + return ( |
| 126 | + <> |
| 127 | + <Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}> |
| 128 | + <SwitchInput label='Enable Post Processing' onChange={handleChange} value={postProcessingEnabled} /> |
| 129 | + </Box> |
| 130 | + <Box sx={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}> |
| 131 | + <Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}> |
| 132 | + <Typography>JS Function</Typography> |
| 133 | + <Button |
| 134 | + sx={{ ml: 2 }} |
| 135 | + variant='outlined' |
| 136 | + onClick={() => { |
| 137 | + setPostProcessingFunction(sampleFunction) |
| 138 | + }} |
| 139 | + > |
| 140 | + See Example |
| 141 | + </Button> |
| 142 | + <div style={{ flex: 1 }} /> |
| 143 | + <IconButton |
| 144 | + size='small' |
| 145 | + sx={{ |
| 146 | + height: 25, |
| 147 | + width: 25 |
| 148 | + }} |
| 149 | + title='Expand' |
| 150 | + color='primary' |
| 151 | + onClick={() => onExpandDialogClicked(postProcessingFunction)} |
| 152 | + > |
| 153 | + <IconArrowsMaximize /> |
| 154 | + </IconButton> |
| 155 | + </Box> |
| 156 | + |
| 157 | + <div |
| 158 | + style={{ |
| 159 | + marginTop: '10px', |
| 160 | + border: '1px solid', |
| 161 | + borderColor: theme.palette.grey['300'], |
| 162 | + borderRadius: '6px', |
| 163 | + height: '200px', |
| 164 | + width: '100%' |
| 165 | + }} |
| 166 | + > |
| 167 | + <CodeEditor |
| 168 | + value={postProcessingFunction} |
| 169 | + height='200px' |
| 170 | + theme={customization.isDarkMode ? 'dark' : 'light'} |
| 171 | + lang={'js'} |
| 172 | + placeholder={sampleFunction} |
| 173 | + onValueChange={(code) => setPostProcessingFunction(code)} |
| 174 | + basicSetup={{ highlightActiveLine: false, highlightActiveLineGutter: false }} |
| 175 | + /> |
| 176 | + </div> |
| 177 | + </Box> |
| 178 | + <div |
| 179 | + style={{ |
| 180 | + display: 'flex', |
| 181 | + flexDirection: 'column', |
| 182 | + borderRadius: 10, |
| 183 | + background: '#d8f3dc', |
| 184 | + padding: 10, |
| 185 | + marginTop: 10 |
| 186 | + }} |
| 187 | + > |
| 188 | + <div |
| 189 | + style={{ |
| 190 | + display: 'flex', |
| 191 | + flexDirection: 'row', |
| 192 | + alignItems: 'center', |
| 193 | + paddingTop: 10 |
| 194 | + }} |
| 195 | + > |
| 196 | + <IconBulb size={30} color='#2d6a4f' /> |
| 197 | + <span style={{ color: '#2d6a4f', marginLeft: 10, fontWeight: 500 }}> |
| 198 | + The following variables are available to use in the custom function:{' '} |
| 199 | + <pre>$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId</pre> |
| 200 | + </span> |
| 201 | + </div> |
| 202 | + </div> |
| 203 | + <StyledButton |
| 204 | + style={{ marginBottom: 10, marginTop: 10 }} |
| 205 | + variant='contained' |
| 206 | + disabled={!postProcessingFunction || postProcessingFunction?.trim().length === 0} |
| 207 | + onClick={onSave} |
| 208 | + > |
| 209 | + Save |
| 210 | + </StyledButton> |
| 211 | + <ExpandTextDialog |
| 212 | + show={showExpandDialog} |
| 213 | + dialogProps={expandDialogProps} |
| 214 | + onCancel={() => setShowExpandDialog(false)} |
| 215 | + onConfirm={(newValue) => { |
| 216 | + setPostProcessingFunction(newValue) |
| 217 | + setShowExpandDialog(false) |
| 218 | + }} |
| 219 | + ></ExpandTextDialog> |
| 220 | + </> |
| 221 | + ) |
| 222 | +} |
| 223 | + |
| 224 | +PostProcessing.propTypes = { |
| 225 | + dialogProps: PropTypes.object |
| 226 | +} |
| 227 | + |
| 228 | +export default PostProcessing |
0 commit comments