mirror of https://github.com/langgenius/dify.git
human input form
This commit is contained in:
parent
81f6344aaa
commit
114dfe038c
|
|
@ -0,0 +1,166 @@
|
|||
'use client'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useParams } from 'next/navigation'
|
||||
import {
|
||||
RiCheckboxCircleFill,
|
||||
RiInformation2Fill,
|
||||
} from '@remixicon/react'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
import Button, { } from '@/app/components/base/button'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
import { MOCK_DATA } from './mock'
|
||||
|
||||
const success = true
|
||||
|
||||
const expired = true
|
||||
|
||||
const submitted = true
|
||||
|
||||
const FormContent = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { token } = useParams()
|
||||
useDocumentTitle('')
|
||||
|
||||
const { site } = MOCK_DATA.site
|
||||
const { form_content, user_actions, timeout, timeout_unit } = MOCK_DATA
|
||||
|
||||
const getButtonStyle = (style: UserActionButtonType) => {
|
||||
if (style === UserActionButtonType.Primary)
|
||||
return 'primary'
|
||||
if (style === UserActionButtonType.Default)
|
||||
return 'secondary'
|
||||
if (style === UserActionButtonType.Accent)
|
||||
return 'secondary-accent'
|
||||
if (style === UserActionButtonType.Ghost)
|
||||
return 'ghost'
|
||||
}
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<div className={cn('flex h-full w-full flex-col items-center justify-center')}>
|
||||
<div className='min-w-[480px] max-w-[640px]'>
|
||||
<div className='border-components-divider-subtle flex h-[320px] flex-col gap-4 rounded-[20px] border bg-chat-bubble-bg p-10 pb-9 shadow-lg backdrop-blur-sm'>
|
||||
<div className='h-[56px] w-[56px] shrink-0 rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge p-3'>
|
||||
<RiCheckboxCircleFill className='h-8 w-8 text-text-success' />
|
||||
</div>
|
||||
<div className='grow'>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.thanks')}</div>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.recorded')}</div>
|
||||
</div>
|
||||
<div className='system-2xs-regular-uppercase shrink-0 text-text-tertiary'>{t('share.humanInput.submissionID', { id: token })}</div>
|
||||
</div>
|
||||
<div className='flex flex-row-reverse px-2 py-3'>
|
||||
<div className={cn(
|
||||
'flex shrink-0 items-center gap-1.5 px-1',
|
||||
)}>
|
||||
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
|
||||
<DifyLogo size='small' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (expired) {
|
||||
return (
|
||||
<div className={cn('flex h-full w-full flex-col items-center justify-center')}>
|
||||
<div className='min-w-[480px] max-w-[640px]'>
|
||||
<div className='border-components-divider-subtle flex h-[320px] flex-col gap-4 rounded-[20px] border bg-chat-bubble-bg p-10 pb-9 shadow-lg backdrop-blur-sm'>
|
||||
<div className='h-[56px] w-[56px] shrink-0 rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge p-3'>
|
||||
<RiInformation2Fill className='h-8 w-8 text-text-accent' />
|
||||
</div>
|
||||
<div className='grow'>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.sorry')}</div>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.expired')}</div>
|
||||
</div>
|
||||
<div className='system-2xs-regular-uppercase shrink-0 text-text-tertiary'>{t('share.humanInput.submissionID', { id: token })}</div>
|
||||
</div>
|
||||
<div className='flex flex-row-reverse px-2 py-3'>
|
||||
<div className={cn(
|
||||
'flex shrink-0 items-center gap-1.5 px-1',
|
||||
)}>
|
||||
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
|
||||
<DifyLogo size='small' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (submitted) {
|
||||
return (
|
||||
<div className={cn('flex h-full w-full flex-col items-center justify-center')}>
|
||||
<div className='min-w-[480px] max-w-[640px]'>
|
||||
<div className='border-components-divider-subtle flex h-[320px] flex-col gap-4 rounded-[20px] border bg-chat-bubble-bg p-10 pb-9 shadow-lg backdrop-blur-sm'>
|
||||
<div className='h-[56px] w-[56px] shrink-0 rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge p-3'>
|
||||
<RiInformation2Fill className='h-8 w-8 text-text-accent' />
|
||||
</div>
|
||||
<div className='grow'>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.sorry')}</div>
|
||||
<div className='title-4xl-semi-bold text-text-primary'>{t('share.humanInput.completed')}</div>
|
||||
</div>
|
||||
<div className='system-2xs-regular-uppercase shrink-0 text-text-tertiary'>{t('share.humanInput.submissionID', { id: token })}</div>
|
||||
</div>
|
||||
<div className='flex flex-row-reverse px-2 py-3'>
|
||||
<div className={cn(
|
||||
'flex shrink-0 items-center gap-1.5 px-1',
|
||||
)}>
|
||||
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
|
||||
<DifyLogo size='small' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('mx-auto flex h-full w-full max-w-[720px] flex-col items-center')}>
|
||||
<div className='mt-4 flex w-full shrink-0 items-center gap-3 py-3'>
|
||||
<AppIcon
|
||||
size='large'
|
||||
iconType={site.icon_type as any}
|
||||
icon={site.icon}
|
||||
background={site.icon_background}
|
||||
imageUrl={site.icon_url}
|
||||
/>
|
||||
<div className='system-xl-semibold grow text-text-primary'>{site.title}</div>
|
||||
</div>
|
||||
<div className='h-0 w-full grow overflow-y-auto'>
|
||||
<div className='border-components-divider-subtle rounded-[20px] border bg-chat-bubble-bg p-4 shadow-lg backdrop-blur-sm'>
|
||||
<Markdown content={form_content || ''} />
|
||||
<div className='flex flex-wrap gap-1 py-1'>
|
||||
{user_actions.map((action: any) => (
|
||||
<Button key={action.id} variant={getButtonStyle(action.button_style) as any}>
|
||||
{action.title}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className='system-xs-regular mt-1 text-text-tertiary'>
|
||||
{timeout_unit === 'day' ? t('share.humanInput.timeoutDay', { count: timeout }) : t('share.humanInput.timeoutHour', { count: timeout })}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-row-reverse px-2 py-3'>
|
||||
<div className={cn(
|
||||
'flex shrink-0 items-center gap-1.5 px-1',
|
||||
)}>
|
||||
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
|
||||
<DifyLogo size='small' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FormContent)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
|
||||
export const MOCK_DATA = {
|
||||
site: {
|
||||
app_id: 'e9823576-d836-4f2b-b46f-bd4df1d82230',
|
||||
end_user_id: 'b7aa295d-1560-4d87-a828-77b3f39b30d0',
|
||||
enable_site: true,
|
||||
site: {
|
||||
title: 'wf',
|
||||
chat_color_theme: null,
|
||||
chat_color_theme_inverted: false,
|
||||
icon_type: 'emoji',
|
||||
icon: '\uD83E\uDD16',
|
||||
icon_background: '#FFEAD5',
|
||||
icon_url: null,
|
||||
description: null,
|
||||
copyright: null,
|
||||
privacy_policy: null,
|
||||
custom_disclaimer: '',
|
||||
default_language: 'en-US',
|
||||
prompt_public: false,
|
||||
show_workflow_steps: true,
|
||||
use_icon_as_answer_icon: false,
|
||||
},
|
||||
model_config: null,
|
||||
plan: 'basic',
|
||||
can_replace_logo: false,
|
||||
custom_config: null,
|
||||
},
|
||||
// 采用与上方 form editor 相同的数据结构,唯一不同的就是
|
||||
// 对于 Text 类型,其文本已完成了变量替换(即,所有使用
|
||||
// {{#node_name.var_name#}} 格式引用其他变量的位置,都
|
||||
// 被替换成了对应的变量的值)
|
||||
//
|
||||
// 参考 FormContent
|
||||
form_content: `
|
||||
# Experiencing the Four Seasons
|
||||

|
||||
|
||||
## My Seasonal Guide
|
||||
Name: {{#noddename.name#}}
|
||||
Location: {{#nodename.location#}}
|
||||
Favorite Season: {{#nodename.season#}}
|
||||
|
||||
The four seasons throughout the year:
|
||||
- Spring: Cherry blossoms, returning birds
|
||||
- Summer: Long sunny days, thunderstorms, beach adventures
|
||||
- Autumn: Red and gold foliage, harvest festivals, apple picking
|
||||
- Winter: Snowfall, frozen lakes, holiday celebrations
|
||||
## Notes
|
||||
|
||||
{{#$output.content#}}
|
||||
`,
|
||||
// 对每一个字段的描述,参考上方 FormInput 的定义。
|
||||
inputs: ['<FormInput>'],
|
||||
// 用户对这个表单可采取的操作,参考上方 UserAction 的定义。
|
||||
user_actions: [
|
||||
{
|
||||
id: 'approve-action',
|
||||
title: 'Post to X',
|
||||
button_style: UserActionButtonType.Primary,
|
||||
},
|
||||
{
|
||||
id: 'regenerate-action',
|
||||
title: 'regenerate',
|
||||
button_style: UserActionButtonType.Default,
|
||||
},
|
||||
{
|
||||
id: 'thinking-action',
|
||||
title: 'thinking',
|
||||
button_style: UserActionButtonType.Accent,
|
||||
},
|
||||
{
|
||||
id: 'cancel-action',
|
||||
title: 'cancel',
|
||||
button_style: UserActionButtonType.Ghost,
|
||||
},
|
||||
],
|
||||
timeout: 3,
|
||||
timeout_unit: 'day',
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
'use client'
|
||||
import React from 'react'
|
||||
import FormContent from './form'
|
||||
|
||||
const FormPage = () => {
|
||||
return (
|
||||
<div className='h-full min-w-[300px] bg-chatbot-bg pb-[env(safe-area-inset-bottom)]'>
|
||||
<FormContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FormPage)
|
||||
|
|
@ -80,6 +80,16 @@ const translation = {
|
|||
login: {
|
||||
backToHome: 'Back to Home',
|
||||
},
|
||||
humanInput: {
|
||||
timeoutDay: 'This action will expire in {{count}} days.',
|
||||
timeoutHour: 'This action will expire in {{count}} hours.',
|
||||
submissionID: 'submission_id: {{id}}',
|
||||
thanks: 'Thanks!',
|
||||
sorry: 'Sorry!',
|
||||
recorded: 'Your input has been recorded.',
|
||||
expired: 'Seems like this request has expired. ',
|
||||
completed: 'Seems like this request was dealt with elsewhere. ',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ const translation = {
|
|||
login: {
|
||||
backToHome: '返回首页',
|
||||
},
|
||||
humanInput: {
|
||||
timeoutDay: '此操作将在 {{count}} 天后过期。',
|
||||
timeoutHour: '此操作将在 {{count}} 小时后过期。',
|
||||
thanks: '谢谢!',
|
||||
sorry: '抱歉!',
|
||||
recorded: '您的输入已被记录。',
|
||||
expired: '此请求似乎已过期。',
|
||||
completed: '此请求似乎在其他地方得到了处理。',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
Loading…
Reference in New Issue