Feat/attachments (#9526)
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
@@ -58,7 +58,7 @@ const ChatWrapper = () => {
|
||||
appConfig,
|
||||
{
|
||||
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
|
||||
promptVariables: inputsForms,
|
||||
inputsForm: inputsForms,
|
||||
},
|
||||
appPrevChatList,
|
||||
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
|
||||
@@ -72,14 +72,12 @@ const ChatWrapper = () => {
|
||||
const doSend: OnSend = useCallback((message, files, last_answer) => {
|
||||
const data: any = {
|
||||
query: message,
|
||||
files,
|
||||
inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
|
||||
conversation_id: currentConversationId,
|
||||
parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
|
||||
}
|
||||
|
||||
if (appConfig?.file_upload?.image.enabled && files?.length)
|
||||
data.files = files
|
||||
|
||||
handleSend(
|
||||
getUrl('chat-messages', isInstalledApp, appId || ''),
|
||||
data,
|
||||
@@ -159,6 +157,8 @@ const ChatWrapper = () => {
|
||||
chatFooterClassName='pb-4'
|
||||
chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}
|
||||
onSend={doSend}
|
||||
inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
|
||||
inputsForm={inputsForms}
|
||||
onRegenerate={doRegenerate}
|
||||
onStopResponding={handleStop}
|
||||
chatNode={chatNode}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { memo } from 'react'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
|
||||
type InputProps = {
|
||||
form: any
|
||||
@@ -23,9 +24,9 @@ const FormInput: FC<InputProps> = ({
|
||||
|
||||
if (type === 'paragraph') {
|
||||
return (
|
||||
<textarea
|
||||
<Textarea
|
||||
value={value}
|
||||
className='grow h-[104px] rounded-lg bg-gray-100 px-2.5 py-2 outline-none appearance-none resize-none'
|
||||
className='resize-none'
|
||||
onChange={e => onChange(variable, e.target.value)}
|
||||
placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
||||
/>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEmbeddedChatbotContext } from '../context'
|
||||
import Input from './form-input'
|
||||
import { PortalSelect } from '@/app/components/base/select'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||
|
||||
const Form = () => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
inputsForms,
|
||||
newConversationInputs,
|
||||
newConversationInputsRef,
|
||||
handleNewConversationInputsChange,
|
||||
isMobile,
|
||||
} = useEmbeddedChatbotContext()
|
||||
|
||||
const handleFormChange = useCallback((variable: string, value: string) => {
|
||||
const handleFormChange = (variable: string, value: any) => {
|
||||
handleNewConversationInputsChange({
|
||||
...newConversationInputs,
|
||||
...newConversationInputsRef.current,
|
||||
[variable]: value,
|
||||
})
|
||||
}, [newConversationInputs, handleNewConversationInputsChange])
|
||||
}
|
||||
|
||||
const renderField = (form: any) => {
|
||||
const {
|
||||
@@ -49,6 +51,46 @@ const Form = () => {
|
||||
)
|
||||
}
|
||||
|
||||
if (form.type === 'number') {
|
||||
return (
|
||||
<input
|
||||
className="grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none"
|
||||
type="number"
|
||||
value={newConversationInputs[variable] || ''}
|
||||
onChange={e => handleFormChange(variable, e.target.value)}
|
||||
placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (form.type === InputVarType.singleFile) {
|
||||
return (
|
||||
<FileUploaderInAttachmentWrapper
|
||||
value={newConversationInputs[variable] ? [newConversationInputs[variable]] : []}
|
||||
onChange={files => handleFormChange(variable, files[0])}
|
||||
fileConfig={{
|
||||
allowed_file_types: form.allowed_file_types,
|
||||
allowed_file_extensions: form.allowed_file_extensions,
|
||||
allowed_file_upload_methods: form.allowed_file_upload_methods,
|
||||
number_limits: 1,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (form.type === InputVarType.multiFiles) {
|
||||
return (
|
||||
<FileUploaderInAttachmentWrapper
|
||||
value={newConversationInputs[variable]}
|
||||
onChange={files => handleFormChange(variable, files)}
|
||||
fileConfig={{
|
||||
allowed_file_types: form.allowed_file_types,
|
||||
allowed_file_extensions: form.allowed_file_extensions,
|
||||
allowed_file_upload_methods: form.allowed_file_upload_methods,
|
||||
number_limits: form.max_length,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PortalSelect
|
||||
popupClassName='w-[200px]'
|
||||
|
||||
@@ -29,6 +29,7 @@ export type EmbeddedChatbotContextValue = {
|
||||
conversationList: AppConversationData['data']
|
||||
showConfigPanelBeforeChat: boolean
|
||||
newConversationInputs: Record<string, any>
|
||||
newConversationInputsRef: RefObject<Record<string, any>>
|
||||
handleNewConversationInputsChange: (v: Record<string, any>) => void
|
||||
inputsForms: any[]
|
||||
handleNewConversation: () => void
|
||||
@@ -51,6 +52,7 @@ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>
|
||||
conversationList: [],
|
||||
showConfigPanelBeforeChat: false,
|
||||
newConversationInputs: {},
|
||||
newConversationInputsRef: { current: {} },
|
||||
handleNewConversationInputsChange: () => {},
|
||||
inputsForms: [],
|
||||
handleNewConversation: () => {},
|
||||
|
||||
@@ -30,6 +30,8 @@ import type {
|
||||
} from '@/models/share'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { changeLanguage } from '@/i18n/i18next-config'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
export const useEmbeddedChatbot = () => {
|
||||
const isInstalledApp = false
|
||||
@@ -94,7 +96,7 @@ export const useEmbeddedChatbot = () => {
|
||||
setNewConversationInputs(newInputs)
|
||||
}, [])
|
||||
const inputsForms = useMemo(() => {
|
||||
return (appParams?.user_input_form || []).filter((item: any) => item.paragraph || item.select || item['text-input'] || item.number).map((item: any) => {
|
||||
return (appParams?.user_input_form || []).filter((item: any) => !item.external_data_tool).map((item: any) => {
|
||||
if (item.paragraph) {
|
||||
let value = initInputs[item.paragraph.variable]
|
||||
if (value && item.paragraph.max_length && value.length > item.paragraph.max_length)
|
||||
@@ -123,6 +125,20 @@ export const useEmbeddedChatbot = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (item['file-list']) {
|
||||
return {
|
||||
...item['file-list'],
|
||||
type: 'file-list',
|
||||
}
|
||||
}
|
||||
|
||||
if (item.file) {
|
||||
return {
|
||||
...item.file,
|
||||
type: 'file',
|
||||
}
|
||||
}
|
||||
|
||||
let value = initInputs[item['text-input'].variable]
|
||||
if (value && item['text-input'].max_length && value.length > item['text-input'].max_length)
|
||||
value = value.slice(0, item['text-input'].max_length)
|
||||
@@ -192,21 +208,38 @@ export const useEmbeddedChatbot = () => {
|
||||
|
||||
const { notify } = useToastContext()
|
||||
const checkInputsRequired = useCallback((silent?: boolean) => {
|
||||
if (inputsForms.length) {
|
||||
for (let i = 0; i < inputsForms.length; i += 1) {
|
||||
const item = inputsForms[i]
|
||||
|
||||
if (item.required && !newConversationInputsRef.current[item.variable]) {
|
||||
if (!silent) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('appDebug.errorMessage.valueOfVarRequired', { key: item.variable }),
|
||||
})
|
||||
}
|
||||
let hasEmptyInput = ''
|
||||
let fileIsUploading = false
|
||||
const requiredVars = inputsForms.filter(({ required }) => required)
|
||||
if (requiredVars.length) {
|
||||
requiredVars.forEach(({ variable, label, type }) => {
|
||||
if (hasEmptyInput)
|
||||
return
|
||||
|
||||
if (fileIsUploading)
|
||||
return
|
||||
|
||||
if (!newConversationInputsRef.current[variable] && !silent)
|
||||
hasEmptyInput = label as string
|
||||
|
||||
if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && newConversationInputsRef.current[variable] && !silent) {
|
||||
const files = newConversationInputsRef.current[variable]
|
||||
if (Array.isArray(files))
|
||||
fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)
|
||||
else
|
||||
fileIsUploading = files.transferMethod === TransferMethod.local_file && !files.uploadedId
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if (hasEmptyInput) {
|
||||
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) })
|
||||
return false
|
||||
}
|
||||
|
||||
if (fileIsUploading) {
|
||||
notify({ type: 'info', message: t('appDebug.errorMessage.waitForFileUpload') })
|
||||
return
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -278,6 +311,7 @@ export const useEmbeddedChatbot = () => {
|
||||
setShowConfigPanelBeforeChat,
|
||||
setShowNewConversationItemInList,
|
||||
newConversationInputs,
|
||||
newConversationInputsRef,
|
||||
handleNewConversationInputsChange,
|
||||
inputsForms,
|
||||
handleNewConversation,
|
||||
|
||||
@@ -124,6 +124,7 @@ const EmbeddedChatbotWrapper = () => {
|
||||
conversationList,
|
||||
showConfigPanelBeforeChat,
|
||||
newConversationInputs,
|
||||
newConversationInputsRef,
|
||||
handleNewConversationInputsChange,
|
||||
inputsForms,
|
||||
handleNewConversation,
|
||||
@@ -151,6 +152,7 @@ const EmbeddedChatbotWrapper = () => {
|
||||
conversationList,
|
||||
showConfigPanelBeforeChat,
|
||||
newConversationInputs,
|
||||
newConversationInputsRef,
|
||||
handleNewConversationInputsChange,
|
||||
inputsForms,
|
||||
handleNewConversation,
|
||||
|
||||
Reference in New Issue
Block a user