feat: replace timeout handling with expiration time in HumanInput form and add ExpirationTime component

This commit is contained in:
twwu 2025-12-25 18:16:32 +08:00
parent be0f493e61
commit e744d4de80
9 changed files with 74 additions and 26 deletions

View File

@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import Button from '@/app/components/base/button'
import ContentItem from '@/app/components/base/chat/chat/answer/human-input-content/content-item'
import ExpirationTime from '@/app/components/base/chat/chat/answer/human-input-content/expiration-time'
import Loading from '@/app/components/base/loading'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
@ -24,8 +25,7 @@ export type FormData = {
form_content: string
inputs: FormInputItem[]
user_actions: UserAction[]
timeout: number
timeout_unit: 'hour' | 'day'
expiration_time: number
}
const FormContent = () => {
@ -249,9 +249,7 @@ const FormContent = () => {
</Button>
))}
</div>
<div className="system-xs-regular mt-1 text-text-tertiary">
{formData.timeout_unit === 'day' ? t('share.humanInput.timeoutDay', { count: formData.timeout }) : t('share.humanInput.timeoutHour', { count: formData.timeout })}
</div>
<ExpirationTime expirationTime={formData.expiration_time} />
</div>
<div className="flex flex-row-reverse px-2 py-3">
<div className={cn(

View File

@ -0,0 +1,24 @@
'use client'
import { useTranslation } from 'react-i18next'
import { useI18N } from '@/context/i18n'
import { formatRelativeTimeInZone } from './utils'
type ExpirationTimeProps = {
expirationTime: number
}
const ExpirationTime = ({
expirationTime,
}: ExpirationTimeProps) => {
const { t } = useTranslation()
const { locale } = useI18N()
const relativeTime = formatRelativeTimeInZone(expirationTime, locale)
return (
<div className="system-xs-regular mt-1 text-text-tertiary">
{t('share.humanInput.expirationTime', { relativeTime })}
</div>
)
}
export default ExpirationTime

View File

@ -2,20 +2,17 @@
import type { HumanInputFormProps } from './type'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import ContentItem from './content-item'
import ExpirationTime from './expiration-time'
import { getButtonStyle, initializeInputs, splitByOutputVar } from './utils'
const HumanInputForm = ({
formData,
showTimeout,
timeout,
timeoutUnit,
onSubmit,
expirationTime,
}: HumanInputFormProps) => {
const { t } = useTranslation()
const formID = formData.form_id
const defaultInputs = initializeInputs(formData.inputs)
const contentList = splitByOutputVar(formData.form_content)
@ -59,10 +56,8 @@ const HumanInputForm = ({
</Button>
))}
</div>
{showTimeout && (
<div className="system-xs-regular mt-1 text-text-tertiary">
{timeoutUnit === 'day' ? t('share.humanInput.timeoutDay', { count: timeout }) : t('share.humanInput.timeoutHour', { count: timeout })}
</div>
{showTimeout && typeof expirationTime === 'number' && (
<ExpirationTime expirationTime={expirationTime} />
)}
</>
)

View File

@ -12,16 +12,14 @@ export type HumanInputContentProps = {
showEmailTip?: boolean
showDebugModeTip?: boolean
showTimeout?: boolean
timeout?: number
timeoutUnit?: 'hour' | 'day'
expirationTime?: number
onSubmit?: (formID: string, data: any) => Promise<void>
}
export type HumanInputFormProps = {
formData: HumanInputFormData
showTimeout?: boolean
timeout?: number
timeoutUnit?: 'hour' | 'day'
expirationTime?: number
onSubmit?: (formID: string, data: any) => Promise<void>
}

View File

@ -1,5 +1,15 @@
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
import type { Locale } from '@/i18n-config'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import utc from 'dayjs/plugin/utc'
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
import 'dayjs/locale/en'
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/ja'
dayjs.extend(utc)
dayjs.extend(relativeTime)
export const getButtonStyle = (style: UserActionButtonType) => {
if (style === UserActionButtonType.Primary)
@ -28,3 +38,21 @@ export const initializeInputs = (formInputs: FormInputItem[]) => {
})
return initialInputs
}
const localeMap: Record<string, string> = {
'en-US': 'en',
'zh-Hans': 'zh-cn',
'ja-JP': 'ja',
}
export const formatRelativeTimeInZone = (
utcTimestamp: string | number,
locale: Locale = 'en-US',
) => {
const dayjsLocale = localeMap[locale] ?? 'en'
return dayjs
.utc(utcTimestamp)
.locale(dayjsLocale)
.fromNow()
}

View File

@ -82,14 +82,13 @@ const translation = {
backToHome: 'Back to Home',
},
humanInput: {
timeoutDay: 'This action will expire in {{count}} days.',
timeoutHour: 'This action will expire in {{count}} hours.',
expirationTime: 'This action will expire {{relativeTime}}.',
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. ',
expired: 'Seems like this request has expired.',
completed: 'Seems like this request was dealt with elsewhere.',
},
}

View File

@ -77,6 +77,14 @@ const translation = {
login: {
backToHome: 'ホームに戻る',
},
humanInput: {
expirationTime: 'この操作は{{relativeTime}}で期限切れになります。',
thanks: 'ありがとうございます!',
sorry: '申し訳ありません!',
recorded: '入力内容は記録されました。',
expired: 'このリクエストは期限切れのようです。',
completed: 'このリクエストは他の場所で処理されたようです。',
},
}
export default translation

View File

@ -78,8 +78,7 @@ const translation = {
backToHome: '返回首页',
},
humanInput: {
timeoutDay: '此操作将在 {{count}} 天后过期。',
timeoutHour: '此操作将在 {{count}} 小时后过期。',
expirationTime: '此操作将在{{relativeTime}}过期。',
thanks: '谢谢!',
sorry: '抱歉!',
recorded: '您的输入已被记录。',

View File

@ -333,8 +333,7 @@ export const getHumanInputForm = (token: string) => {
form_content: string
inputs: FormInputItem[]
user_actions: UserAction[]
timeout: number
timeout_unit: 'hour' | 'day'
expiration_time: number
}>(`/api/form/human_input/${token}`)
}