dify
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import React, { memo, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEmbeddedChatbotContext } from '../context'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
import { PortalSelect } from '@/app/components/base/select'
|
||||
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
|
||||
type Props = {
|
||||
showTip?: boolean
|
||||
}
|
||||
|
||||
const InputsFormContent = ({ showTip }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
appParams,
|
||||
inputsForms,
|
||||
currentConversationId,
|
||||
currentConversationInputs,
|
||||
setCurrentConversationInputs,
|
||||
newConversationInputs,
|
||||
newConversationInputsRef,
|
||||
handleNewConversationInputsChange,
|
||||
} = useEmbeddedChatbotContext()
|
||||
const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputs
|
||||
|
||||
const handleFormChange = useCallback((variable: string, value: any) => {
|
||||
setCurrentConversationInputs({
|
||||
...currentConversationInputs,
|
||||
[variable]: value,
|
||||
})
|
||||
handleNewConversationInputsChange({
|
||||
...newConversationInputsRef.current,
|
||||
[variable]: value,
|
||||
})
|
||||
}, [newConversationInputsRef, handleNewConversationInputsChange, currentConversationInputs, setCurrentConversationInputs])
|
||||
|
||||
const visibleInputsForms = inputsForms.filter(form => form.hide !== true)
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
{visibleInputsForms.map(form => (
|
||||
<div key={form.variable} className='space-y-1'>
|
||||
{form.type !== InputVarType.checkbox && (
|
||||
<div className='flex h-6 items-center gap-1'>
|
||||
<div className='system-md-semibold text-text-secondary'>{form.label}</div>
|
||||
{!form.required && (
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{form.type === InputVarType.textInput && (
|
||||
<Input
|
||||
value={inputsFormValue?.[form.variable] || ''}
|
||||
onChange={e => handleFormChange(form.variable, e.target.value)}
|
||||
placeholder={form.label}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.number && (
|
||||
<Input
|
||||
type='number'
|
||||
value={inputsFormValue?.[form.variable] || ''}
|
||||
onChange={e => handleFormChange(form.variable, e.target.value)}
|
||||
placeholder={form.label}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.paragraph && (
|
||||
<Textarea
|
||||
value={inputsFormValue?.[form.variable] || ''}
|
||||
onChange={e => handleFormChange(form.variable, e.target.value)}
|
||||
placeholder={form.label}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.checkbox && (
|
||||
<BoolInput
|
||||
name={form.label}
|
||||
value={inputsFormValue?.[form.variable]}
|
||||
required={form.required}
|
||||
onChange={value => handleFormChange(form.variable, value)}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.select && (
|
||||
<PortalSelect
|
||||
popupClassName='w-[200px]'
|
||||
value={inputsFormValue?.[form.variable] ?? form.default ?? ''}
|
||||
items={form.options.map((option: string) => ({ value: option, name: option }))}
|
||||
onSelect={item => handleFormChange(form.variable, item.value as string)}
|
||||
placeholder={form.label}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.singleFile && (
|
||||
<FileUploaderInAttachmentWrapper
|
||||
value={inputsFormValue?.[form.variable] ? [inputsFormValue?.[form.variable]] : []}
|
||||
onChange={files => handleFormChange(form.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,
|
||||
fileUploadConfig: (appParams as any).system_parameters,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.multiFiles && (
|
||||
<FileUploaderInAttachmentWrapper
|
||||
value={inputsFormValue?.[form.variable] || []}
|
||||
onChange={files => handleFormChange(form.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,
|
||||
fileUploadConfig: (appParams as any).system_parameters,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{form.type === InputVarType.jsonObject && (
|
||||
<CodeEditor
|
||||
language={CodeLanguage.json}
|
||||
value={inputsFormValue?.[form.variable] || ''}
|
||||
onChange={v => handleFormChange(form.variable, v)}
|
||||
noWrapper
|
||||
className='bg h-[80px] overflow-y-auto rounded-[10px] bg-components-input-bg-normal p-1'
|
||||
placeholder={
|
||||
<div className='whitespace-pre'>{form.json_schema}</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{showTip && (
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('share.chat.chatFormTip')}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(InputsFormContent)
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Message3Fill } from '@/app/components/base/icons/src/public/other'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import InputsFormContent from '@/app/components/base/chat/embedded-chatbot/inputs-form/content'
|
||||
import { useEmbeddedChatbotContext } from '../context'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
collapsed: boolean
|
||||
setCollapsed: (collapsed: boolean) => void
|
||||
}
|
||||
|
||||
const InputsFormNode = ({
|
||||
collapsed,
|
||||
setCollapsed,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
isMobile,
|
||||
currentConversationId,
|
||||
themeBuilder,
|
||||
handleStartChat,
|
||||
allInputsHidden,
|
||||
inputsForms,
|
||||
} = useEmbeddedChatbotContext()
|
||||
|
||||
if (allInputsHidden || inputsForms.length === 0)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn('mb-6 flex flex-col items-center px-4 pt-6', isMobile && 'mb-4 pt-4')}>
|
||||
<div className={cn(
|
||||
'w-full max-w-[672px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-md',
|
||||
collapsed && 'border border-components-card-border bg-components-card-bg shadow-none',
|
||||
)}>
|
||||
<div className={cn(
|
||||
'flex items-center gap-3 rounded-t-2xl px-6 py-4',
|
||||
!collapsed && 'border-b border-divider-subtle',
|
||||
isMobile && 'px-4 py-3',
|
||||
)}>
|
||||
<Message3Fill className='h-6 w-6 shrink-0' />
|
||||
<div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div>
|
||||
{collapsed && (
|
||||
<Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(false)}>{t('common.operation.edit')}</Button>
|
||||
)}
|
||||
{!collapsed && currentConversationId && (
|
||||
<Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(true)}>{t('common.operation.close')}</Button>
|
||||
)}
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className={cn('p-6', isMobile && 'p-4')}>
|
||||
<InputsFormContent />
|
||||
</div>
|
||||
)}
|
||||
{!collapsed && !currentConversationId && (
|
||||
<div className={cn('p-6', isMobile && 'p-4')}>
|
||||
<Button
|
||||
variant='primary'
|
||||
className='w-full'
|
||||
onClick={() => handleStartChat(() => setCollapsed(true))}
|
||||
style={
|
||||
themeBuilder?.theme
|
||||
? {
|
||||
backgroundColor: themeBuilder?.theme.primaryColor,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>{t('share.chat.startChat')}</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{collapsed && (
|
||||
<div className='flex w-full max-w-[720px] items-center py-4'>
|
||||
<Divider bgStyle='gradient' className='h-px basis-1/2 rotate-180' />
|
||||
<Divider bgStyle='gradient' className='h-px basis-1/2' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InputsFormNode
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiChatSettingsLine,
|
||||
} from '@remixicon/react'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
|
||||
import { Message3Fill } from '@/app/components/base/icons/src/public/other'
|
||||
import InputsFormContent from '@/app/components/base/chat/embedded-chatbot/inputs-form/content'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
iconColor?: string
|
||||
}
|
||||
const ViewFormDropdown = ({ iconColor }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-end'
|
||||
offset={{
|
||||
mainAxis: 4,
|
||||
crossAxis: 4,
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => setOpen(v => !v)}
|
||||
>
|
||||
<ActionButton size='l' state={open ? ActionButtonState.Hover : ActionButtonState.Default}>
|
||||
<RiChatSettingsLine className={cn('h-[18px] w-[18px]', iconColor)} />
|
||||
</ActionButton>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className="z-50">
|
||||
<div className='w-[400px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg backdrop-blur-sm'>
|
||||
<div className='flex items-center gap-3 rounded-t-2xl border-b border-divider-subtle px-6 py-4'>
|
||||
<Message3Fill className='h-6 w-6 shrink-0' />
|
||||
<div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div>
|
||||
</div>
|
||||
<div className='p-6'>
|
||||
<InputsFormContent />
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewFormDropdown
|
||||
Reference in New Issue
Block a user