diff --git a/web/app/(humanInputLayout)/form/[token]/form.tsx b/web/app/(humanInputLayout)/form/[token]/form.tsx index e1455361fb..0853799ffb 100644 --- a/web/app/(humanInputLayout)/form/[token]/form.tsx +++ b/web/app/(humanInputLayout)/form/[token]/form.tsx @@ -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 = () => { ))} -
- {formData.timeout_unit === 'day' ? t('share.humanInput.timeoutDay', { count: formData.timeout }) : t('share.humanInput.timeoutHour', { count: formData.timeout })} -
+
{ + const { t } = useTranslation() + const { locale } = useI18N() + const relativeTime = formatRelativeTimeInZone(expirationTime, locale) + + return ( +
+ {t('share.humanInput.expirationTime', { relativeTime })} +
+ ) +} + +export default ExpirationTime diff --git a/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx b/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx index 72b66173bc..a05b29a018 100644 --- a/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx +++ b/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx @@ -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 = ({ ))}
- {showTimeout && ( -
- {timeoutUnit === 'day' ? t('share.humanInput.timeoutDay', { count: timeout }) : t('share.humanInput.timeoutHour', { count: timeout })} -
+ {showTimeout && typeof expirationTime === 'number' && ( + )} ) diff --git a/web/app/components/base/chat/chat/answer/human-input-content/type.ts b/web/app/components/base/chat/chat/answer/human-input-content/type.ts index 9687ff52ef..563b24c005 100644 --- a/web/app/components/base/chat/chat/answer/human-input-content/type.ts +++ b/web/app/components/base/chat/chat/answer/human-input-content/type.ts @@ -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 } export type HumanInputFormProps = { formData: HumanInputFormData showTimeout?: boolean - timeout?: number - timeoutUnit?: 'hour' | 'day' + expirationTime?: number onSubmit?: (formID: string, data: any) => Promise } diff --git a/web/app/components/base/chat/chat/answer/human-input-content/utils.ts b/web/app/components/base/chat/chat/answer/human-input-content/utils.ts index 9408dd94d1..d00633a940 100644 --- a/web/app/components/base/chat/chat/answer/human-input-content/utils.ts +++ b/web/app/components/base/chat/chat/answer/human-input-content/utils.ts @@ -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 = { + '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() +} diff --git a/web/i18n/en-US/share.ts b/web/i18n/en-US/share.ts index a7aa965a0e..6fff1b9bb2 100644 --- a/web/i18n/en-US/share.ts +++ b/web/i18n/en-US/share.ts @@ -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.', }, } diff --git a/web/i18n/ja-JP/share.ts b/web/i18n/ja-JP/share.ts index 07d6ae3e45..54bdfc48ec 100644 --- a/web/i18n/ja-JP/share.ts +++ b/web/i18n/ja-JP/share.ts @@ -77,6 +77,14 @@ const translation = { login: { backToHome: 'ホームに戻る', }, + humanInput: { + expirationTime: 'この操作は{{relativeTime}}で期限切れになります。', + thanks: 'ありがとうございます!', + sorry: '申し訳ありません!', + recorded: '入力内容は記録されました。', + expired: 'このリクエストは期限切れのようです。', + completed: 'このリクエストは他の場所で処理されたようです。', + }, } export default translation diff --git a/web/i18n/zh-Hans/share.ts b/web/i18n/zh-Hans/share.ts index 876d59267b..b06bdfc4bc 100644 --- a/web/i18n/zh-Hans/share.ts +++ b/web/i18n/zh-Hans/share.ts @@ -78,8 +78,7 @@ const translation = { backToHome: '返回首页', }, humanInput: { - timeoutDay: '此操作将在 {{count}} 天后过期。', - timeoutHour: '此操作将在 {{count}} 小时后过期。', + expirationTime: '此操作将在{{relativeTime}}过期。', thanks: '谢谢!', sorry: '抱歉!', recorded: '您的输入已被记录。', diff --git a/web/service/share.ts b/web/service/share.ts index 8c39cc765f..fa72d2cf2e 100644 --- a/web/service/share.ts +++ b/web/service/share.ts @@ -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}`) }