mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 09:00:52 +03:00
Fix merge conflicts
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useSelector } from 'react-redux'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
const StatsCard = ({ title, stat }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
return (
|
||||
<Card sx={{ border: '1px solid #e0e0e0', borderRadius: `${customization.borderRadius}px` }}>
|
||||
<CardContent>
|
||||
<Typography sx={{ fontSize: 14 }} color='text.primary' gutterBottom>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: 30, fontWeight: 500 }} color='text.primary'>
|
||||
{stat}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
StatsCard.propTypes = {
|
||||
title: PropTypes.string,
|
||||
stat: PropTypes.string
|
||||
}
|
||||
|
||||
export default StatsCard
|
||||
@@ -0,0 +1,107 @@
|
||||
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'
|
||||
import { IconX } from '@tabler/icons'
|
||||
|
||||
// Project import
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import { SwitchInput } from 'ui-component/switch/Switch'
|
||||
|
||||
// store
|
||||
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 variant='contained' onClick={onSave}>
|
||||
Save
|
||||
</StyledButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ChatFeedback.propTypes = {
|
||||
dialogProps: PropTypes.object
|
||||
}
|
||||
|
||||
export default ChatFeedback
|
||||
@@ -5,6 +5,7 @@ import { Box, Dialog, DialogContent, DialogTitle, Tabs, Tab } from '@mui/materia
|
||||
import SpeechToText from './SpeechToTextDialog'
|
||||
import Configuration from 'views/chatflows/Configuration'
|
||||
import AllowedDomains from './AllowedDomainsDialog'
|
||||
import ChatFeedback from './ChatFeedbackDialog'
|
||||
|
||||
const CHATFLOW_CONFIGURATION_TABS = [
|
||||
{
|
||||
@@ -86,6 +87,7 @@ const ChatflowConfigurationDialog = ({ show, dialogProps, onCancel }) => {
|
||||
<TabPanel key={index} value={tabValue} index={index}>
|
||||
{item.id === 'rateLimiting' && <Configuration />}
|
||||
{item.id === 'speechToText' ? <SpeechToText dialogProps={dialogProps} /> : null}
|
||||
{item.id === 'chatFeedback' ? <ChatFeedback dialogProps={dialogProps} /> : null}
|
||||
{item.id === 'allowedDomains' ? <AllowedDomains dialogProps={dialogProps} /> : null}
|
||||
</TabPanel>
|
||||
))}
|
||||
|
||||
@@ -39,12 +39,15 @@ import { CodeBlock } from 'ui-component/markdown/CodeBlock'
|
||||
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog'
|
||||
import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown'
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import StatsCard from 'ui-component/cards/StatsCard'
|
||||
import Feedback from 'ui-component/extended/Feedback'
|
||||
|
||||
// store
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
|
||||
|
||||
// API
|
||||
import chatmessageApi from 'api/chatmessage'
|
||||
import feedbackApi from 'api/feedback'
|
||||
import useApi from 'hooks/useApi'
|
||||
import useConfirm from 'hooks/useConfirm'
|
||||
|
||||
@@ -91,6 +94,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const [chatlogs, setChatLogs] = useState([])
|
||||
const [allChatlogs, setAllChatLogs] = useState([])
|
||||
const [chatMessages, setChatMessages] = useState([])
|
||||
const [stats, setStats] = useState([])
|
||||
const [selectedMessageIndex, setSelectedMessageIndex] = useState(0)
|
||||
const [sourceDialogOpen, setSourceDialogOpen] = useState(false)
|
||||
const [sourceDialogProps, setSourceDialogProps] = useState({})
|
||||
@@ -100,6 +104,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
|
||||
const getChatmessageApi = useApi(chatmessageApi.getAllChatmessageFromChatflow)
|
||||
const getChatmessageFromPKApi = useApi(chatmessageApi.getChatmessageFromPK)
|
||||
const getStatsApi = useApi(feedbackApi.getStatsFromChatflow)
|
||||
const getStoragePathFromServer = useApi(chatmessageApi.getStoragePath)
|
||||
let storagePath = ''
|
||||
|
||||
@@ -162,6 +167,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
if (chatmsg.sourceDocuments) msg.sourceDocuments = JSON.parse(chatmsg.sourceDocuments)
|
||||
if (chatmsg.usedTools) msg.usedTools = JSON.parse(chatmsg.usedTools)
|
||||
if (chatmsg.fileAnnotations) msg.fileAnnotations = JSON.parse(chatmsg.fileAnnotations)
|
||||
if (chatmsg.feedback) msg.feedback = chatmsg.feedback?.content
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(obj, chatPK)) {
|
||||
obj[chatPK] = {
|
||||
@@ -238,6 +244,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
}
|
||||
})
|
||||
getChatmessageApi.request(chatflowid)
|
||||
getStatsApi.request(chatflowid) // update stats
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
@@ -405,9 +412,16 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getChatmessageApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (getStatsApi.data) {
|
||||
setStats(getStatsApi.data)
|
||||
}
|
||||
}, [getStatsApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.chatflow) {
|
||||
getChatmessageApi.request(dialogProps.chatflow.id)
|
||||
getStatsApi.request(dialogProps.chatflow.id)
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -449,7 +463,33 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%', marginBottom: 10 }}>
|
||||
<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',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
marginLeft: 8,
|
||||
marginRight: 8
|
||||
}}
|
||||
>
|
||||
<div style={{ marginRight: 10 }}>
|
||||
<b style={{ marginRight: 10 }}>From Date</b>
|
||||
<DatePicker
|
||||
@@ -812,6 +852,12 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{message.type === 'apiMessage' && message.feedback ? (
|
||||
<Feedback
|
||||
content={message.feedback?.content || ''}
|
||||
rating={message.feedback?.rating}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Alert, IconButton } from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const ThumbsUpIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='20'
|
||||
height='20'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
>
|
||||
<path d='M7 10v12' />
|
||||
<path d='M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const ThumbsDownIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='20'
|
||||
height='20'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
>
|
||||
<path d='M17 14V2' />
|
||||
<path d='M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const Feedback = ({ content, rating }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'start' }}>
|
||||
{content ? (
|
||||
<Alert
|
||||
icon={rating === 'THUMBS_UP' ? <ThumbsUpIcon /> : <ThumbsDownIcon />}
|
||||
severity={rating === 'THUMBS_UP' ? 'success' : 'error'}
|
||||
style={{ marginBottom: 14 }}
|
||||
variant='outlined'
|
||||
>
|
||||
{content ? <span style={{ color: theme.palette.text.primary }}>{content}</span> : null}
|
||||
</Alert>
|
||||
) : (
|
||||
<IconButton color={rating === 'THUMBS_UP' ? 'success' : 'error'} style={{ marginBottom: 14 }}>
|
||||
{rating === 'THUMBS_UP' ? <ThumbsUpIcon /> : <ThumbsDownIcon />}
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Feedback.propTypes = {
|
||||
rating: PropTypes.oneOf(['THUMBS_UP', 'THUMBS_DOWN']),
|
||||
content: PropTypes.string
|
||||
}
|
||||
|
||||
export default Feedback
|
||||
@@ -1,13 +1,21 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl, Switch } from '@mui/material'
|
||||
import { FormControl, Switch, Typography } from '@mui/material'
|
||||
|
||||
export const SwitchInput = ({ value, onChange, disabled = false }) => {
|
||||
export const SwitchInput = ({ label, value, onChange, disabled = false }) => {
|
||||
const [myValue, setMyValue] = useState(!!value ?? false)
|
||||
|
||||
useEffect(() => {
|
||||
setMyValue(value)
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<FormControl
|
||||
sx={{ mt: 1, width: '100%', display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}
|
||||
size='small'
|
||||
>
|
||||
{label && <Typography>{label}</Typography>}
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
checked={myValue}
|
||||
@@ -22,6 +30,7 @@ export const SwitchInput = ({ value, onChange, disabled = false }) => {
|
||||
}
|
||||
|
||||
SwitchInput.propTypes = {
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
|
||||
Reference in New Issue
Block a user