| | |
| | | import React, { useEffect, useState } from 'react' |
| | | import { useTranslation } from 'react-i18next' |
| | | import { useDebounce, useGetState } from 'ahooks' |
| | | import { RiSettings2Line } from '@remixicon/react' |
| | | import produce from 'immer' |
| | | import { LinkExternal02 } from '../../base/icons/src/vender/line/general' |
| | | import { LinkExternal02, Settings01 } from '../../base/icons/src/vender/line/general' |
| | | import type { Credential, CustomCollectionBackend, CustomParamSchema, Emoji } from '../types' |
| | | import { AuthHeaderPrefix, AuthType } from '../types' |
| | | import GetSchema from './get-schema' |
| | |
| | | import LabelSelector from '@/app/components/tools/labels/selector' |
| | | import Toast from '@/app/components/base/toast' |
| | | |
| | | const fieldNameClassNames = 'py-2 leading-5 text-sm font-medium text-gray-900' |
| | | type Props = { |
| | | positionLeft?: boolean |
| | | payload: any |
| | |
| | | setCustomCollection(newCollection) |
| | | setParamsSchemas(parameters_schema) |
| | | } |
| | | catch { |
| | | catch (e) { |
| | | const customCollection = getCustomCollection() |
| | | const newCollection = produce(customCollection, (draft) => { |
| | | draft.schema_type = '' |
| | |
| | | const path = decodeURI(new URL(url).pathname) |
| | | return path || '' |
| | | } |
| | | catch { |
| | | catch (e) { |
| | | return url |
| | | } |
| | | } |
| | |
| | | panelClassName='mt-2 !w-[640px]' |
| | | maxWidthClassName='!max-w-[640px]' |
| | | height='calc(100vh - 16px)' |
| | | headerClassName='!border-b-divider-regular' |
| | | headerClassName='!border-b-black/5' |
| | | body={ |
| | | <div className='flex h-full flex-col'> |
| | | <div className='h-0 grow space-y-4 overflow-y-auto px-6 py-3'> |
| | | <div className='flex flex-col h-full'> |
| | | <div className='grow h-0 overflow-y-auto px-6 py-3 space-y-4'> |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.name')} <span className='ml-1 text-red-500'>*</span></div> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.name')} <span className='ml-1 text-red-500'>*</span></div> |
| | | <div className='flex items-center justify-between gap-3'> |
| | | <AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} className='cursor-pointer' icon={emoji.content} background={emoji.background} /> |
| | | <Input |
| | |
| | | |
| | | {/* Schema */} |
| | | <div className='select-none'> |
| | | <div className='flex items-center justify-between'> |
| | | <div className='flex justify-between items-center'> |
| | | <div className='flex items-center'> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.schema')}<span className='ml-1 text-red-500'>*</span></div> |
| | | <div className='mx-2 h-3 w-px bg-divider-regular'></div> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.schema')}<span className='ml-1 text-red-500'>*</span></div> |
| | | <div className='mx-2 w-px h-3 bg-black/5'></div> |
| | | <a |
| | | href="https://swagger.io/specification/" |
| | | target='_blank' rel='noopener noreferrer' |
| | | className='flex h-[18px] items-center space-x-1 text-text-accent' |
| | | className='flex items-center h-[18px] space-x-1 text-[#155EEF]' |
| | | > |
| | | <div className='text-xs font-normal'>{t('tools.createTool.viewSchemaSpec')}</div> |
| | | <LinkExternal02 className='h-3 w-3' /> |
| | | <LinkExternal02 className='w-3 h-3' /> |
| | | </a> |
| | | </div> |
| | | <GetSchema onChange={setSchema} /> |
| | |
| | | |
| | | {/* Available Tools */} |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.availableTools.title')}</div> |
| | | <div className='w-full overflow-x-auto rounded-lg border border-divider-regular'> |
| | | <table className='system-xs-regular w-full text-text-secondary'> |
| | | <thead className='uppercase text-text-tertiary'> |
| | | <tr className={cn(paramsSchemas.length > 0 && 'border-b', 'border-divider-regular')}> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.availableTools.title')}</div> |
| | | <div className='rounded-lg border border-gray-200 w-full overflow-x-auto'> |
| | | <table className='w-full leading-[18px] text-xs text-gray-700 font-normal'> |
| | | <thead className='text-gray-500 uppercase'> |
| | | <tr className={cn(paramsSchemas.length > 0 && 'border-b', 'border-gray-200')}> |
| | | <th className="p-2 pl-3 font-medium">{t('tools.createTool.availableTools.name')}</th> |
| | | <th className="w-[236px] p-2 pl-3 font-medium">{t('tools.createTool.availableTools.description')}</th> |
| | | <th className="p-2 pl-3 font-medium w-[236px]">{t('tools.createTool.availableTools.description')}</th> |
| | | <th className="p-2 pl-3 font-medium">{t('tools.createTool.availableTools.method')}</th> |
| | | <th className="p-2 pl-3 font-medium">{t('tools.createTool.availableTools.path')}</th> |
| | | <th className="w-[54px] p-2 pl-3 font-medium">{t('tools.createTool.availableTools.action')}</th> |
| | | <th className="p-2 pl-3 font-medium w-[54px]">{t('tools.createTool.availableTools.action')}</th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | {paramsSchemas.map((item, index) => ( |
| | | <tr key={index} className='border-b border-divider-regular last:border-0'> |
| | | <tr key={index} className='border-b last:border-0 border-gray-200'> |
| | | <td className="p-2 pl-3">{item.operation_id}</td> |
| | | <td className="w-[236px] p-2 pl-3">{item.summary}</td> |
| | | <td className="p-2 pl-3 text-gray-500 w-[236px]">{item.summary}</td> |
| | | <td className="p-2 pl-3">{item.method}</td> |
| | | <td className="p-2 pl-3">{getPath(item.server_url)}</td> |
| | | <td className="w-[62px] p-2 pl-3"> |
| | | <td className="p-2 pl-3 w-[62px]"> |
| | | <Button |
| | | size='small' |
| | | onClick={() => { |
| | |
| | | |
| | | {/* Authorization method */} |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.authMethod.title')}</div> |
| | | <div className='flex h-9 cursor-pointer items-center justify-between rounded-lg bg-components-input-bg-normal px-2.5' onClick={() => setCredentialsModalShow(true)}> |
| | | <div className='system-xs-regular text-text-primary'>{t(`tools.createTool.authMethod.types.${credential.auth_type}`)}</div> |
| | | <RiSettings2Line className='h-4 w-4 text-text-secondary' /> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.authMethod.title')}</div> |
| | | <div className='flex items-center h-9 justify-between px-2.5 bg-gray-100 rounded-lg cursor-pointer' onClick={() => setCredentialsModalShow(true)}> |
| | | <div className='text-sm font-normal text-gray-900'>{t(`tools.createTool.authMethod.types.${credential.auth_type}`)}</div> |
| | | <Settings01 className='w-4 h-4 text-gray-700 opacity-60' /> |
| | | </div> |
| | | </div> |
| | | |
| | | {/* Labels */} |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.toolInput.label')}</div> |
| | | <div className='py-2 leading-5 text-sm font-medium text-gray-900'>{t('tools.createTool.toolInput.label')}</div> |
| | | <LabelSelector value={labels} onChange={handleLabelSelect} /> |
| | | </div> |
| | | |
| | | {/* Privacy Policy */} |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.privacyPolicy')}</div> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.privacyPolicy')}</div> |
| | | <Input |
| | | value={customCollection.privacy_policy} |
| | | onChange={(e) => { |
| | |
| | | </div> |
| | | |
| | | <div> |
| | | <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.customDisclaimer')}</div> |
| | | <div className={fieldNameClassNames}>{t('tools.createTool.customDisclaimer')}</div> |
| | | <Input |
| | | value={customCollection.custom_disclaimer} |
| | | onChange={(e) => { |
| | |
| | | </div> |
| | | |
| | | </div> |
| | | <div className={cn(isEdit ? 'justify-between' : 'justify-end', 'mt-2 flex shrink-0 rounded-b-[10px] border-t border-divider-regular bg-background-section-burn px-6 py-4')} > |
| | | <div className={cn(isEdit ? 'justify-between' : 'justify-end', 'mt-2 shrink-0 flex py-4 px-6 rounded-b-[10px] bg-gray-50 border-t border-black/5')} > |
| | | { |
| | | isEdit && ( |
| | | <Button variant='warning' onClick={onRemove}>{t('common.operation.delete')}</Button> |
| | | <Button onClick={onRemove} className='text-red-500 border-red-50 hover:border-red-500'>{t('common.operation.delete')}</Button> |
| | | ) |
| | | } |
| | | <div className='flex space-x-2 '> |