feat(web): skill toolList add is_active
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useEffect, type FC } from 'react';
|
||||
import { Layout } from 'antd';
|
||||
|
||||
import { useUser } from '@/store/user';
|
||||
|
||||
@@ -35,10 +36,10 @@ const BasicAuthLayout: FC = () => {
|
||||
}, [getUserInfo]);
|
||||
|
||||
return (
|
||||
<div className="rb:relative rb:min-h-screen rb:w-screen">
|
||||
<Layout className="rb:min-h-screen!">
|
||||
{/* Render child routes without additional UI */}
|
||||
<Outlet />
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import { type FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, List } from 'antd'
|
||||
import { Space, Button, Flex } from 'antd'
|
||||
|
||||
import Card from '@/views/ApplicationConfig/components/Card'
|
||||
import type {
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
import Empty from '@/components/Empty'
|
||||
import ToolModal from './ToolModal'
|
||||
import { getToolMethods, getToolDetail } from '@/api/tools'
|
||||
import Tag from '@/components/Tag'
|
||||
|
||||
/**
|
||||
* Tool List Component Props
|
||||
@@ -61,6 +62,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
const mcpFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
|
||||
return {
|
||||
...item,
|
||||
is_active: (toolDetail as any).is_active,
|
||||
label: mcpFilterItem?.description,
|
||||
method_id: mcpFilterItem?.method_id,
|
||||
value: mcpFilterItem?.name,
|
||||
@@ -74,6 +76,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
const builtinFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
|
||||
return {
|
||||
...item,
|
||||
is_active: (toolDetail as any).is_active,
|
||||
label: builtinFilterItem?.description,
|
||||
method_id: builtinFilterItem?.method_id,
|
||||
value: builtinFilterItem?.name,
|
||||
@@ -84,6 +87,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
// Single method: Use first method
|
||||
return {
|
||||
...item,
|
||||
is_active: (toolDetail as any).is_active,
|
||||
label: (methods as any[])[0]?.description,
|
||||
method_id: (methods as any[])[0]?.method_id,
|
||||
value: (methods as any[])[0]?.name,
|
||||
@@ -96,6 +100,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
const customFilterItem = (methods as any[]).find(vo => vo.method_id === item.operation)
|
||||
return {
|
||||
...item,
|
||||
is_active: (toolDetail as any).is_active,
|
||||
label: customFilterItem?.name,
|
||||
method_id: customFilterItem?.method_id,
|
||||
value: customFilterItem?.name,
|
||||
@@ -129,7 +134,10 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
* @param tool - Tool to add
|
||||
*/
|
||||
const updateTools = (tool: ToolOption) => {
|
||||
const list = [...toolList, tool]
|
||||
const list = [...toolList, {
|
||||
...tool,
|
||||
is_active: true,
|
||||
}]
|
||||
setToolList(list)
|
||||
onChange && onChange(list)
|
||||
}
|
||||
@@ -146,42 +154,35 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
<Card
|
||||
title={t('application.toolConfiguration')}
|
||||
extra={
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleAddTool}>
|
||||
+ {t('application.addTool')}
|
||||
</Button>
|
||||
<Button className="rb:h-6! rb:py-0! rb:px-2! rb:rounded-md! rb:text-[#21233" onClick={handleAddTool}>+ {t('application.addTool')}</Button>
|
||||
}
|
||||
>
|
||||
{/* Show empty state or tool list */}
|
||||
{toolList.length === 0
|
||||
? <Empty size={88} />
|
||||
:
|
||||
<List
|
||||
grid={{ gutter: 12, column: 1 }}
|
||||
dataSource={toolList}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
{/* Tool card with delete button */}
|
||||
<div key={index} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
{/* Tool label/description */}
|
||||
<div className="rb:font-medium rb:leading-4">
|
||||
{item.label}
|
||||
</div>
|
||||
<Space size={12}>
|
||||
{/* Delete button with hover effect */}
|
||||
<div
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteTool(index)}
|
||||
></div>
|
||||
</Space>
|
||||
? <div className="rb-border rb:rounded-xl rb:pt-4 rb:pb-6"><Empty size={88} /></div>
|
||||
: <Flex vertical gap={12}>
|
||||
{toolList.map((item, index) => (
|
||||
<Flex key={index} align="center" justify="space-between" className="rb:py-2.5! rb:pl-4! rb:pr-3! rb-border rb:rounded-lg">
|
||||
<div>
|
||||
<div className="rb:font-medium rb:leading-4">
|
||||
{item.label}
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
<Tag color={item.is_active ? 'success' : 'error'} className="rb:mt-1">
|
||||
{item.is_active ? t('common.enable') : t('common.deleted')}
|
||||
</Tag>
|
||||
</div>
|
||||
<Space size={12}>
|
||||
<div
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteTool(index)}
|
||||
></div>
|
||||
</Space>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
}
|
||||
{/* Tool selection modal */}
|
||||
<ToolModal
|
||||
ref={toolModalRef}
|
||||
refresh={updateTools}
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface ToolOption {
|
||||
tool_id?: string;
|
||||
/** Whether tool is enabled */
|
||||
enabled?: boolean;
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,17 +7,16 @@
|
||||
import { type FC, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Form, Input, Button, Space, Select, App } from 'antd'
|
||||
import { Form, Input, Button, Space, Select, App, Flex } from 'antd'
|
||||
|
||||
import Card from '@/views/ApplicationConfig/components/Card'
|
||||
import aiPrompt from '@/assets/images/application/aiPrompt.png'
|
||||
import AiPromptModal from '@/views/ApplicationConfig/components/AiPromptModal'
|
||||
import ToolList from '../components/ToolList/ToolList'
|
||||
import type { AiPromptModalRef } from '@/views/ApplicationConfig/types'
|
||||
import exitIcon from '@/assets/images/knowledgeBase/exit.png';
|
||||
import type { SkillFormData } from '../types'
|
||||
import { getSkillDetail, createSkill, updateSkill } from '@/api/skill'
|
||||
import { stringRegExp } from '@/utils/validator';
|
||||
import PageHeader from '@/components/Layout/PageHeader'
|
||||
|
||||
/**
|
||||
* Skill Configuration Page Component
|
||||
@@ -43,6 +42,7 @@ const SkillConfig: FC = () => {
|
||||
const { message } = App.useApp()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [form] = Form.useForm<SkillFormData>();
|
||||
const [data, setData] = useState<SkillFormData | null>(null)
|
||||
|
||||
/**
|
||||
* Effect: Load skill data if editing existing skill
|
||||
@@ -70,6 +70,7 @@ const SkillConfig: FC = () => {
|
||||
getSkillDetail(id)
|
||||
.then(res => {
|
||||
form.setFieldsValue(res as SkillFormData)
|
||||
setData(res as SkillFormData)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
@@ -131,93 +132,103 @@ const SkillConfig: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:w-250 rb:mt-5 rb:pb-5 rb:mx-auto">
|
||||
{/* Back button */}
|
||||
<div className='rb:flex rb:items-center rb:gap-2 rb:mb-4 rb:cursor-pointer' onClick={handleBack}>
|
||||
<img src={exitIcon} alt='exit' className='rb:w-4 rb:h-4' />
|
||||
<span className='rb:text-gray-500 rb:text-sm'>{t('common.exit')}</span>
|
||||
</div>
|
||||
|
||||
<Form form={form} layout="vertical">
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
{/* Manifest Section: Basic skill information */}
|
||||
<Card title={t('skills.mainfest')}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label={t('skills.name')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.inputPlaceholder', { title: t('skills.name') }) },
|
||||
{ max: 50 },
|
||||
{ pattern: stringRegExp, message: t('common.nameInvalid') },
|
||||
]}
|
||||
<Flex vertical className="rb:h-screen!">
|
||||
<PageHeader
|
||||
title={data?.name}
|
||||
extra={
|
||||
<Flex gap={12} align="center">
|
||||
{/* Save button */}
|
||||
<Button type="primary" className="rb:px-2! rb:gap-0.5!" disabled={loading} onClick={handleSave}>{t('skills.save')}</Button>
|
||||
<Button
|
||||
className="rb:px-2! rb:gap-0.5!"
|
||||
icon={<div className="rb:bg-[url('@/assets/images/workflow/return.svg')] rb:size-4 rb:bg-cover"></div>}
|
||||
onClick={handleBack}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="description"
|
||||
label={t('skills.description')}
|
||||
rules={[{ max: 500 }]}
|
||||
>
|
||||
<Input.TextArea placeholder={t('skills.descriptionPlaceholder')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['config', 'keywords']}
|
||||
label={t('skills.keywords')}
|
||||
rules={[{ required: true, message: t('common.inputPlaceholder', { title: t('skills.keywords') }) }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
{t('common.return')}
|
||||
</Button>
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
<div className="rb:w-250 rb:my-3 rb:mx-auto rb:flex-1 rb:overflow-y-auto">
|
||||
<Form form={form} layout="vertical">
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
{/* Manifest Section: Basic skill information */}
|
||||
<Card title={t('skills.mainfest')}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label={t('skills.name')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.inputPlaceholder', { title: t('skills.name') }) },
|
||||
{ max: 50 },
|
||||
{ pattern: stringRegExp, message: t('common.nameInvalid') },
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="description"
|
||||
label={t('skills.description')}
|
||||
rules={[{ max: 500 }]}
|
||||
>
|
||||
<Input.TextArea placeholder={t('skills.descriptionPlaceholder')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['config', 'keywords']}
|
||||
label={t('skills.keywords')}
|
||||
rules={[{ required: true, message: t('common.inputPlaceholder', { title: t('skills.keywords') }) }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
{/* Prompt Configuration Section: AI instructions */}
|
||||
<Card title={t('skills.promptConfiguration')}
|
||||
extra={
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handlePrompt}>
|
||||
<img src={aiPrompt} className="rb:size-5" />
|
||||
{t('skills.aiPrompt')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{/* Prompt Configuration Section: AI instructions */}
|
||||
<Card title={t('skills.promptConfiguration')}
|
||||
extra={
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handlePrompt}>
|
||||
<div className="rb:size-5 rb:bg-cover rb:bg-[url('@/assets/images/application/aiPrompt.png')] rb:mr-1!" />
|
||||
{t('skills.aiPrompt')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
name="prompt"
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder={t('skills.promptPlaceholder')}
|
||||
styles={{
|
||||
textarea: {
|
||||
minHeight: '200px',
|
||||
borderRadius: '8px'
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
{/* Tool Configuration Section */}
|
||||
<Form.Item
|
||||
name="prompt"
|
||||
name="tools"
|
||||
rules={[{ required: true, message: t('common.selectPlaceholder', { title: t('skills.tools') }) }]}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder={t('skills.promptPlaceholder')}
|
||||
styles={{
|
||||
textarea: {
|
||||
minHeight: '200px',
|
||||
borderRadius: '8px'
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ToolList />
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
{/* Tool Configuration Section */}
|
||||
<Form.Item
|
||||
name="tools"
|
||||
rules={[{ required: true, message: t('common.selectPlaceholder', { title: t('skills.tools') }) }]}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<ToolList />
|
||||
</Form.Item>
|
||||
|
||||
{/* Save button */}
|
||||
<Button type="primary" block disabled={loading} onClick={handleSave}>{t('skills.save')}</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
|
||||
{/* AI Prompt Generation Modal */}
|
||||
<AiPromptModal
|
||||
ref={aiPromptModalRef}
|
||||
refresh={updatePrompt}
|
||||
source="skills"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</Form>
|
||||
|
||||
{/* AI Prompt Generation Modal */}
|
||||
<AiPromptModal
|
||||
ref={aiPromptModalRef}
|
||||
refresh={updatePrompt}
|
||||
source="skills"
|
||||
/>
|
||||
</div>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user