diff --git a/web/app/components/base/modal/modal.tsx b/web/app/components/base/modal/modal.tsx new file mode 100644 index 0000000000..3137e7adcc --- /dev/null +++ b/web/app/components/base/modal/modal.tsx @@ -0,0 +1,118 @@ +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import { RiCloseLine } from '@remixicon/react' +import { + PortalToFollowElem, + PortalToFollowElemContent, +} from '@/app/components/base/portal-to-follow-elem' +import Button from '@/app/components/base/button' +import type { ButtonProps } from '@/app/components/base/button' +import cn from '@/utils/classnames' + +type ModalProps = { + onClose?: () => void + size?: 'sm' | 'md' + title: string + subTitle?: string + children?: React.ReactNode + confirmButtonText?: string + onConfirm?: () => void + cancelButtonText?: string + onCancel?: () => void + showExtraButton?: boolean + extraButtonText?: string + extraButtonVariant?: ButtonProps['variant'] + onExtraButtonClick?: () => void + footerSlot?: React.ReactNode + bottomSlot?: React.ReactNode +} +const Modal = ({ + onClose, + size = 'sm', + title, + subTitle, + children, + confirmButtonText, + onConfirm, + cancelButtonText, + onCancel, + showExtraButton, + extraButtonVariant = 'warning', + extraButtonText, + onExtraButtonClick, + footerSlot, + bottomSlot, +}: ModalProps) => { + const { t } = useTranslation() + + return ( + + + e.stopPropagation()} + > + + {title} + { + subTitle && ( + + {subTitle} + + ) + } + + + + + { + children && ( + {children} + ) + } + + {footerSlot} + { + showExtraButton && ( + <> + + {extraButtonText || t('common.operation.remove')} + + + > + ) + } + + {cancelButtonText || t('common.operation.cancel')} + + + {confirmButtonText || t('common.operation.save')} + + + {bottomSlot} + + + + ) +} + +export default memo(Modal) diff --git a/web/app/components/tools/tool-auth/add-api-key-button.tsx b/web/app/components/tools/tool-auth/add-api-key-button.tsx new file mode 100644 index 0000000000..21eeed5600 --- /dev/null +++ b/web/app/components/tools/tool-auth/add-api-key-button.tsx @@ -0,0 +1,40 @@ +import { + memo, + useState, +} from 'react' +import Button from '@/app/components/base/button' +import type { ButtonProps } from '@/app/components/base/button' +import ApiKeyModal from './api-key-modal' + +type AddApiKeyButtonProps = { + buttonVariant?: ButtonProps['variant'] + buttonText?: string +} +const AddApiKeyButton = ({ + buttonVariant = 'secondary-accent', + buttonText = 'use api key', +}: AddApiKeyButtonProps) => { + const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false) + + return ( + <> + setIsApiKeyModalOpen(true)} + > + {buttonText} + + { + isApiKeyModalOpen && ( + setIsApiKeyModalOpen(false)} + /> + ) + } + > + + ) +} + +export default memo(AddApiKeyButton) diff --git a/web/app/components/tools/tool-auth/add-oauth-button.tsx b/web/app/components/tools/tool-auth/add-oauth-button.tsx new file mode 100644 index 0000000000..7e6bc91774 --- /dev/null +++ b/web/app/components/tools/tool-auth/add-oauth-button.tsx @@ -0,0 +1,69 @@ +import { + memo, + useState, +} from 'react' +import { RiEqualizer2Line } from '@remixicon/react' +import Button from '@/app/components/base/button' +import type { ButtonProps } from '@/app/components/base/button' +import OAuthClientSettings from './oauth-client-settings' +import cn from '@/utils/classnames' + +type AddOAuthButtonProps = { + buttonVariant?: ButtonProps['variant'] + buttonText?: string + className?: string + buttonLeftClassName?: string + buttonRightClassName?: string + dividerClassName?: string +} +const AddOAuthButton = ({ + buttonVariant = 'primary', + buttonText = 'use oauth', + className, + buttonLeftClassName, + buttonRightClassName, + dividerClassName, +}: AddOAuthButtonProps) => { + const [isOAuthSettingsOpen, setIsOAuthSettingsOpen] = useState(false) + + return ( + <> + + + {buttonText} + + + setIsOAuthSettingsOpen(true)} + > + + + + { + isOAuthSettingsOpen && ( + setIsOAuthSettingsOpen(false)} + /> + ) + } + > + ) +} + +export default memo(AddOAuthButton) diff --git a/web/app/components/tools/tool-auth/api-key-modal.tsx b/web/app/components/tools/tool-auth/api-key-modal.tsx new file mode 100644 index 0000000000..f4a2332861 --- /dev/null +++ b/web/app/components/tools/tool-auth/api-key-modal.tsx @@ -0,0 +1,52 @@ +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import { RiExternalLinkLine } from '@remixicon/react' +import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' +import Modal from '@/app/components/base/modal/modal' + +export type ApiKeyModalProps = { + onClose?: () => void +} +const ApiKeyModal = ({ + onClose, +}: ApiKeyModalProps) => { + const { t } = useTranslation() + + return ( + + Get your API Key from OpenAI + + + } + bottomSlot={ + + + {t('common.modelProvider.encrypted.front')} + + PKCS1_OAEP + + {t('common.modelProvider.encrypted.back')} + + } + > + oauth + + ) +} + +export default memo(ApiKeyModal) diff --git a/web/app/components/tools/tool-auth/authorization.tsx b/web/app/components/tools/tool-auth/authorization.tsx new file mode 100644 index 0000000000..5d3322d629 --- /dev/null +++ b/web/app/components/tools/tool-auth/authorization.tsx @@ -0,0 +1,124 @@ +import { + memo, + useState, +} from 'react' +import { + RiArrowDownSLine, + RiDeleteBinLine, + RiEditLine, +} from '@remixicon/react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import Button from '@/app/components/base/button' +import Indicator from '@/app/components/header/indicator' +import Badge from '@/app/components/base/badge' +import ActionButton from '@/app/components/base/action-button' +import cn from '@/utils/classnames' +import AddOauthButton from './add-oauth-button' +import AddApiKeyButton from './add-api-key-button' + +const Authorization = () => { + const [isOpen, setIsOpen] = useState(false) + + return ( + + setIsOpen(!isOpen)} + asChild + > + + + 4 Authorizations + + + + + + + + + OAuth + + + + + + Auth 1 + + + Default + + + + + + + + + + + + + + + API Keys + + + + + + Production + + + + + 0.2 + + + ENTERPRISE + + + + + + + + + + + + + + ) +} + +export default memo(Authorization) diff --git a/web/app/components/tools/tool-auth/index.tsx b/web/app/components/tools/tool-auth/index.tsx new file mode 100644 index 0000000000..a632029ca2 --- /dev/null +++ b/web/app/components/tools/tool-auth/index.tsx @@ -0,0 +1,23 @@ +import { + memo, +} from 'react' +import AddOAuthButton from './add-oauth-button' +import AddApiKeyButton from './add-api-key-button' + +const ToolAuth = () => { + return ( + <> + + + + + or + + + + + > + ) +} + +export default memo(ToolAuth) diff --git a/web/app/components/tools/tool-auth/oauth-client-settings.tsx b/web/app/components/tools/tool-auth/oauth-client-settings.tsx new file mode 100644 index 0000000000..724351bd57 --- /dev/null +++ b/web/app/components/tools/tool-auth/oauth-client-settings.tsx @@ -0,0 +1,26 @@ +import { memo } from 'react' +import Modal from '@/app/components/base/modal/modal' + +type OAuthClientSettingsProps = { + onClose?: () => void +} +const OAuthClientSettings = ({ + onClose, +}: OAuthClientSettingsProps) => { + return ( + + oauth + + ) +} + +export default memo(OAuthClientSettings) diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 038159870e..561a6e8eea 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -14,6 +14,8 @@ import Loading from '@/app/components/base/loading' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' import { Type } from '../llm/types' +import ToolAuth from '@/app/components/tools/tool-auth' +import Authorization from '@/app/components/tools/tool-auth/authorization' const i18nPrefix = 'workflow.nodes.tool' @@ -53,6 +55,10 @@ const Panel: FC> = ({ return ( + + + + {!readOnly && isShowAuthBtn && ( <>