mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 13:00:56 +03:00
Fix merge conflicts
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { IconButton } from '@mui/material'
|
||||
import { IconClipboard } from '@tabler/icons'
|
||||
|
||||
const CopyToClipboardButton = (props) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
disabled={props.isDisabled || props.isLoading}
|
||||
onClick={props.onClick}
|
||||
size='small'
|
||||
sx={{ background: 'transparent', border: 'none' }}
|
||||
title='Copy to clipboard'
|
||||
>
|
||||
<IconClipboard
|
||||
style={{ width: '20px', height: '20px' }}
|
||||
color={props.isLoading ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
CopyToClipboardButton.propTypes = {
|
||||
isDisabled: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
export default CopyToClipboardButton
|
||||
@@ -0,0 +1,31 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { IconButton } from '@mui/material'
|
||||
import { IconThumbDown } from '@tabler/icons'
|
||||
|
||||
const ThumbsDownButton = (props) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
return (
|
||||
<IconButton
|
||||
disabled={props.isDisabled || props.isLoading}
|
||||
onClick={props.onClick}
|
||||
size='small'
|
||||
sx={{ background: 'transparent', border: 'none' }}
|
||||
title='Thumbs Down'
|
||||
>
|
||||
<IconThumbDown
|
||||
style={{ width: '20px', height: '20px' }}
|
||||
color={props.rating === 'THUMBS_DOWN' ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
ThumbsDownButton.propTypes = {
|
||||
isDisabled: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
rating: PropTypes.string
|
||||
}
|
||||
|
||||
export default ThumbsDownButton
|
||||
@@ -0,0 +1,31 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { IconButton } from '@mui/material'
|
||||
import { IconThumbUp } from '@tabler/icons'
|
||||
|
||||
const ThumbsUpButton = (props) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
return (
|
||||
<IconButton
|
||||
disabled={props.isDisabled || props.isLoading}
|
||||
onClick={props.onClick}
|
||||
size='small'
|
||||
sx={{ background: 'transparent', border: 'none' }}
|
||||
title='Thumbs Up'
|
||||
>
|
||||
<IconThumbUp
|
||||
style={{ width: '20px', height: '20px' }}
|
||||
color={props.rating === 'THUMBS_UP' ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
ThumbsUpButton.propTypes = {
|
||||
isDisabled: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
rating: PropTypes.string
|
||||
}
|
||||
|
||||
export default ThumbsUpButton
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
||||
|
||||
// material-ui
|
||||
import { Button, Box } from '@mui/material'
|
||||
@@ -12,6 +11,7 @@ import { StyledButton } from '@/ui-component/button/StyledButton'
|
||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
||||
|
||||
// store
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
||||
import useNotifier from '@/utils/useNotifier'
|
||||
|
||||
// API
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { Button, Dialog, DialogContent, DialogTitle, DialogActions, Box, OutlinedInput } from '@mui/material'
|
||||
import { useState } from 'react'
|
||||
|
||||
// Project import
|
||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
||||
|
||||
// store
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
||||
|
||||
const ChatFeedbackContentDialog = ({ show, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [feedbackContent, setFeedbackContent] = useState('')
|
||||
|
||||
const onChange = useCallback((e) => setFeedbackContent(e.target.value), [setFeedbackContent])
|
||||
|
||||
const onSave = () => {
|
||||
onConfirm(feedbackContent)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
return () => {
|
||||
dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
setFeedbackContent('')
|
||||
}
|
||||
}, [show, dispatch])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='sm'
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
Provide additional feedback
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<OutlinedInput
|
||||
// eslint-disable-next-line
|
||||
autoFocus
|
||||
id='feedbackContentInput'
|
||||
multiline={true}
|
||||
name='feedbackContentInput'
|
||||
onChange={onChange}
|
||||
placeholder='What do you think of the response?'
|
||||
rows={4}
|
||||
value={feedbackContent}
|
||||
sx={{ width: '100%' }}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<StyledButton variant='contained' onClick={onSave}>
|
||||
Submit Feedback
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
export default ChatFeedbackContentDialog
|
||||
@@ -0,0 +1,107 @@
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
// material-ui
|
||||
import { Button, Box } from '@mui/material'
|
||||
import { IconX } from '@tabler/icons'
|
||||
|
||||
// Project import
|
||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
||||
|
||||
// store
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
||||
import useNotifier from '@/utils/useNotifier'
|
||||
|
||||
// API
|
||||
import chatflowsApi from '@/api/chatflows'
|
||||
|
||||
const ChatFeedback = ({ dialogProps }) => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
useNotifier()
|
||||
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const [chatFeedbackStatus, setChatFeedbackStatus] = useState(false)
|
||||
const [chatbotConfig, setChatbotConfig] = useState({})
|
||||
|
||||
const handleChange = (value) => {
|
||||
setChatFeedbackStatus(value)
|
||||
}
|
||||
|
||||
const onSave = async () => {
|
||||
try {
|
||||
let value = {
|
||||
chatFeedback: {
|
||||
status: chatFeedbackStatus
|
||||
}
|
||||
}
|
||||
chatbotConfig.chatFeedback = value.chatFeedback
|
||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
||||
chatbotConfig: JSON.stringify(chatbotConfig)
|
||||
})
|
||||
if (saveResp.data) {
|
||||
enqueueSnackbar({
|
||||
message: 'Chat Feedback Settings Saved',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data })
|
||||
}
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
message: `Failed to save Chat Feedback Settings: ${errorData}`,
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) {
|
||||
let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig)
|
||||
setChatbotConfig(chatbotConfig || {})
|
||||
if (chatbotConfig.chatFeedback) {
|
||||
setChatFeedbackStatus(chatbotConfig.chatFeedback.status)
|
||||
}
|
||||
}
|
||||
|
||||
return () => {}
|
||||
}, [dialogProps])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<SwitchInput label='Enable chat feedback' onChange={handleChange} value={chatFeedbackStatus} />
|
||||
</Box>
|
||||
<StyledButton style={{ marginBottom: 10, marginTop: 10 }} variant='contained' onClick={onSave}>
|
||||
Save
|
||||
</StyledButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ChatFeedback.propTypes = {
|
||||
dialogProps: PropTypes.object
|
||||
}
|
||||
|
||||
export default ChatFeedback
|
||||
@@ -115,6 +115,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
endDate: endDate,
|
||||
chatType: chatTypeFilter.length ? chatTypeFilter : undefined
|
||||
})
|
||||
getStatsApi.request(dialogProps.chatflow.id, {
|
||||
startDate: date,
|
||||
endDate: endDate,
|
||||
chatType: chatTypeFilter.length ? chatTypeFilter : undefined
|
||||
})
|
||||
}
|
||||
|
||||
const onEndDateSelected = (date) => {
|
||||
@@ -124,6 +129,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
startDate: startDate,
|
||||
chatType: chatTypeFilter.length ? chatTypeFilter : undefined
|
||||
})
|
||||
getStatsApi.request(dialogProps.chatflow.id, {
|
||||
endDate: date,
|
||||
startDate: startDate,
|
||||
chatType: chatTypeFilter.length ? chatTypeFilter : undefined
|
||||
})
|
||||
}
|
||||
|
||||
const onChatTypeSelected = (chatTypes) => {
|
||||
@@ -133,6 +143,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
})
|
||||
getStatsApi.request(dialogProps.chatflow.id, {
|
||||
chatType: chatTypes.length ? chatTypes : undefined,
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
})
|
||||
}
|
||||
|
||||
const exportMessages = async () => {
|
||||
@@ -432,6 +447,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
setSelectedMessageIndex(0)
|
||||
setStartDate(new Date().setMonth(new Date().getMonth() - 1))
|
||||
setEndDate(new Date())
|
||||
setStats([])
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -463,23 +479,6 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
||||
gap: 10,
|
||||
marginBottom: 16,
|
||||
marginLeft: 8,
|
||||
marginRight: 8
|
||||
}}
|
||||
>
|
||||
<StatsCard title='Total Messages (API/Embed)' stat={`${stats.totalMessages}`} />
|
||||
<StatsCard title='Total Feedback Received' stat={`${stats.totalFeedback}`} />
|
||||
<StatsCard
|
||||
title='Positive Feedback'
|
||||
stat={`${((stats.positiveFeedback / stats.totalFeedback) * 100 || 0).toFixed(2)}%`}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
@@ -536,6 +535,23 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
</div>
|
||||
<div style={{ flex: 1 }}></div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
||||
gap: 10,
|
||||
marginBottom: 16,
|
||||
marginLeft: 8,
|
||||
marginRight: 8
|
||||
}}
|
||||
>
|
||||
<StatsCard title='Total Messages' stat={`${stats.totalMessages}`} />
|
||||
<StatsCard title='Total Feedback Received' stat={`${stats.totalFeedback}`} />
|
||||
<StatsCard
|
||||
title='Positive Feedback'
|
||||
stat={`${((stats.positiveFeedback / stats.totalFeedback) * 100 || 0).toFixed(2)}%`}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
{chatlogs && chatlogs.length == 0 && (
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center', width: '100%' }} flexDirection='column'>
|
||||
|
||||
Reference in New Issue
Block a user