dify/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

162 lines
6.1 KiB
TypeScript

'use client'
import type { FC } from 'react'
import type { FormSchema } from '../../base/form/types'
import type { PluginDetail } from '../types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
Drawer,
DrawerBackdrop,
DrawerContent,
DrawerPopup,
DrawerPortal,
DrawerViewport,
} from '@langgenius/dify-ui/drawer'
import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import { ReadmeEntrance } from '../readme-panel/entrance'
type Props = Readonly<{
formSchemas: FormSchema[]
defaultValues?: any
onCancel: () => void
onSaved: (value: Record<string, any>) => void
pluginDetail: PluginDetail
}>
const extractDefaultValues = (schemas: any[]) => {
const result: Record<string, any> = {}
for (const field of schemas) {
if (field.default !== undefined) result[field.name] = field.default
}
return result
}
const EndpointModal: FC<Props> = ({
formSchemas,
defaultValues = {},
onCancel,
onSaved,
pluginDetail,
}) => {
const getValueFromI18nObject = useRenderI18nObject()
const { t } = useTranslation()
const initialValues =
Object.keys(defaultValues).length > 0 ? defaultValues : extractDefaultValues(formSchemas)
const [tempCredential, setTempCredential] = React.useState<any>(initialValues)
const handleSave = () => {
for (const field of formSchemas) {
if (field.required && !tempCredential[field.name]) {
toast.error(
t(($) => $['errorMsg.fieldRequired'], {
ns: 'common',
field:
typeof field.label === 'string'
? field.label
: getValueFromI18nObject(field.label as Record<string, string>),
}),
)
return
}
}
// Fix: Process boolean fields to ensure they are sent as proper boolean values
const processedCredential = { ...tempCredential }
formSchemas.forEach((field: any) => {
if (field.type === 'boolean' && processedCredential[field.name] !== undefined) {
const value = processedCredential[field.name]
if (typeof value === 'string')
processedCredential[field.name] = value === 'true' || value === '1' || value === 'True'
else if (typeof value === 'number') processedCredential[field.name] = value === 1
else if (typeof value === 'boolean') processedCredential[field.name] = value
}
})
onSaved(processedCredential)
}
return (
<Drawer
open
modal
swipeDirection="right"
onOpenChange={(open) => {
if (!open) onCancel()
}}
>
<DrawerPortal>
<DrawerBackdrop className="bg-black/30" />
<DrawerViewport>
<DrawerPopup
className={cn(
'justify-start bg-components-panel-bg! p-0! shadow-xl data-[swipe-direction=right]:top-2 data-[swipe-direction=right]:right-2 data-[swipe-direction=right]:bottom-2 data-[swipe-direction=right]:h-[calc(100dvh-16px)] data-[swipe-direction=right]:w-[400px] data-[swipe-direction=right]:max-w-[calc(100vw-1rem)] data-[swipe-direction=right]:rounded-2xl data-[swipe-direction=right]:border-[0.5px] data-[swipe-direction=right]:border-components-panel-border',
)}
>
<DrawerContent className="flex min-h-0 flex-1 flex-col p-0 pb-0">
<div className="p-4 pb-2">
<div className="flex items-center justify-between">
<div className="system-xl-semibold text-text-primary">
{t(($) => $['detailPanel.endpointModalTitle'], { ns: 'plugin' })}
</div>
<ActionButton onClick={onCancel}>
<RiCloseLine className="size-4" />
</ActionButton>
</div>
<div className="mt-0.5 system-xs-regular text-text-tertiary">
{t(($) => $['detailPanel.endpointModalDesc'], { ns: 'plugin' })}
</div>
<ReadmeEntrance pluginDetail={pluginDetail} className="px-0 pt-3" />
</div>
<div className="grow overflow-y-auto">
<div className="px-4 py-2">
<Form
value={tempCredential}
onChange={(v) => {
setTempCredential(v)
}}
formSchemas={formSchemas as any}
isEditMode={true}
showOnVariableMap={{}}
validating={false}
inputClassName="bg-components-input-bg-normal hover:bg-components-input-bg-hover"
fieldMoreInfo={(item) =>
item.url ? (
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center body-xs-regular text-text-accent-secondary"
>
{t(($) => $.howToGet, { ns: 'tools' })}
<RiArrowRightUpLine className="ml-1 size-3" />
</a>
) : null
}
/>
</div>
<div className={cn('flex justify-end p-4 pt-0')}>
<div className="flex gap-2">
<Button onClick={onCancel}>
{t(($) => $['operation.cancel'], { ns: 'common' })}
</Button>
<Button variant="primary" onClick={handleSave}>
{t(($) => $['operation.save'], { ns: 'common' })}
</Button>
</div>
</div>
</div>
</DrawerContent>
</DrawerPopup>
</DrawerViewport>
</DrawerPortal>
</Drawer>
)
}
export default React.memo(EndpointModal)