'use client' import type { ButtonProps } from '@/app/components/base/button' import type { FormInputItem, UserAction } from '@/app/components/workflow/nodes/human-input/types' import type { SiteInfo } from '@/models/share' import type { HumanInputFormError } from '@/service/use-share' import { RiCheckboxCircleFill, RiErrorWarningFill, RiInformation2Fill, } from '@remixicon/react' import { produce } from 'immer' import * as React from 'react' import { useEffect, useMemo, useState } from 'react' 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 { getButtonStyle } from '@/app/components/base/chat/chat/answer/human-input-content/utils' import Loading from '@/app/components/base/loading' import DifyLogo from '@/app/components/base/logo/dify-logo' import useDocumentTitle from '@/hooks/use-document-title' import { useParams } from '@/next/navigation' import { useGetHumanInputForm, useSubmitHumanInputForm } from '@/service/use-share' import { cn } from '@/utils/classnames' export type FormData = { site: { site: SiteInfo } form_content: string inputs: FormInputItem[] resolved_default_values: Record user_actions: UserAction[] expiration_time: number } const FormContent = () => { const { t } = useTranslation() const { token } = useParams<{ token: string }>() useDocumentTitle('') const [inputs, setInputs] = useState>({}) const [success, setSuccess] = useState(false) const { mutate: submitForm, isPending: isSubmitting } = useSubmitHumanInputForm() const { data: formData, isLoading, error } = useGetHumanInputForm(token) const expired = (error as HumanInputFormError | null)?.code === 'human_input_form_expired' const submitted = (error as HumanInputFormError | null)?.code === 'human_input_form_submitted' const rateLimitExceeded = (error as HumanInputFormError | null)?.code === 'web_form_rate_limit_exceeded' const splitByOutputVar = (content: string): string[] => { const outputVarRegex = /(\{\{#\$output\.[^#]+#\}\})/g const parts = content.split(outputVarRegex) return parts.filter(part => part.length > 0) } const contentList = useMemo(() => { if (!formData?.form_content) return [] return splitByOutputVar(formData.form_content) }, [formData?.form_content]) useEffect(() => { if (!formData?.inputs) return const initialInputs: Record = {} formData.inputs.forEach((item) => { initialInputs[item.output_variable_name] = item.default.type === 'variable' ? formData.resolved_default_values[item.output_variable_name] || '' : item.default.value }) setInputs(initialInputs) }, [formData?.inputs, formData?.resolved_default_values]) // use immer const handleInputsChange = (name: string, value: string) => { const newInputs = produce(inputs, (draft) => { draft[name] = value }) setInputs(newInputs) } const submit = (actionID: string) => { submitForm( { token, data: { inputs, action: actionID } }, { onSuccess: () => { setSuccess(true) }, }, ) } if (isLoading) { return ( ) } if (success) { return (
{t('humanInput.thanks', { ns: 'share' })}
{t('humanInput.recorded', { ns: 'share' })}
{t('humanInput.submissionID', { id: token, ns: 'share' })}
{t('chat.poweredBy', { ns: 'share' })}
) } if (expired) { return (
{t('humanInput.sorry', { ns: 'share' })}
{t('humanInput.expired', { ns: 'share' })}
{t('humanInput.submissionID', { id: token, ns: 'share' })}
{t('chat.poweredBy', { ns: 'share' })}
) } if (submitted) { return (
{t('humanInput.sorry', { ns: 'share' })}
{t('humanInput.completed', { ns: 'share' })}
{t('humanInput.submissionID', { id: token, ns: 'share' })}
{t('chat.poweredBy', { ns: 'share' })}
) } if (rateLimitExceeded) { return (
{t('humanInput.rateLimitExceeded', { ns: 'share' })}
{t('chat.poweredBy', { ns: 'share' })}
) } if (!formData) { return (
{t('humanInput.formNotFound', { ns: 'share' })}
{t('chat.poweredBy', { ns: 'share' })}
) } const site = formData.site.site return (
{site.title}
{contentList.map((content, index) => ( ))}
{formData.user_actions.map((action: UserAction) => ( ))}
{t('chat.poweredBy', { ns: 'share' })}
) } export default React.memo(FormContent)