feat: template modal

This commit is contained in:
scarqin
2024-12-25 10:14:07 +08:00
parent 6c997c0b51
commit 21164859cf
3 changed files with 199 additions and 86 deletions
@@ -0,0 +1,160 @@
import { $t } from '@common/locales'
import { DatePicker, Form, Input, Modal, Switch, theme, Typography } from 'antd'
import dayjs from 'dayjs'
import React, { useEffect, useState } from 'react'
interface ApiKeyModalProps {
visible: boolean
onCancel: () => void
onSave: (values: any) => void
vendorName: string
mode: 'add' | 'edit'
initialValues?: {
keyName?: string
apiKey?: any
expirationDate?: string
enabled?: boolean
}
defaultKeyNumber?: number
}
const { TextArea } = Input
const { Text } = Typography
const ApiKeyModal: React.FC<ApiKeyModalProps> = ({
visible,
onCancel,
onSave,
vendorName,
mode,
initialValues,
defaultKeyNumber = 1
}) => {
const [form] = Form.useForm()
const { token } = theme.useToken()
const [neverExpire, setNeverExpire] = useState(true)
useEffect(() => {
if (visible) {
if (mode === 'add') {
form.setFieldsValue({
keyName: `KEY${defaultKeyNumber}`,
neverExpire: true,
expirationDate: dayjs().add(7, 'days'),
apiKey: {
openai_api_base: 'API Base',
openai_api_key: 'API Key'
}
})
} else if (initialValues) {
form.setFieldsValue({
keyName: initialValues.keyName,
apiKey: initialValues.apiKey,
expirationDate: initialValues.expirationDate ? dayjs(initialValues.expirationDate) : undefined,
enabled: initialValues.enabled,
neverExpire: !initialValues.expirationDate
})
setNeverExpire(!initialValues.expirationDate)
}
}
}, [visible, mode, initialValues, defaultKeyNumber, form])
const handleOk = async () => {
try {
const values = await form.validateFields()
onSave({
...values,
expirationDate: neverExpire ? null : values.expirationDate.format('YYYY-MM-DD HH:mm:ss')
})
} catch (error) {
console.error('Validation failed:', error)
}
}
const handleNeverExpireChange = (checked: boolean) => {
setNeverExpire(checked)
if (!checked) {
form.setFieldsValue({
expirationDate: dayjs().add(7, 'days')
})
}
}
return (
<Modal
title={mode === 'add' ? $t('Add {{vendorName}} APIKEY', { vendorName }) : $t('Edit API Key')}
open={visible}
onCancel={onCancel}
onOk={handleOk}
destroyOnClose
width={600}
>
<Form form={form} layout="vertical">
<Form.Item
name="keyName"
label={$t('* KEY Name')}
rules={[{ required: true, message: $t('Please input the KEY name') }]}
>
<Input disabled={mode === 'edit'} />
</Form.Item>
<Form.Item
name="apiKey"
label={
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<span>{$t('* API KEY')}</span>
{mode === 'add' && (
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer">
{$t('Get API KEY from OpenAI')}
</a>
)}
</div>
}
rules={[{ required: true, message: $t('Please input the API key') }]}
>
{mode === 'add' ? (
<TextArea
rows={4}
placeholder={JSON.stringify(
{
openai_api_base: 'API Base',
openai_api_key: 'API Key'
},
null,
2
)}
/>
) : (
<Input.Password />
)}
</Form.Item>
<Form.Item name="neverExpire" valuePropName="checked">
<Switch
checkedChildren={$t('Never Expire')}
unCheckedChildren={$t('Set Expiration')}
onChange={handleNeverExpireChange}
/>
</Form.Item>
{!neverExpire && (
<Form.Item
name="expirationDate"
label={$t('Expiration Date')}
rules={[{ required: true, message: $t('Please select expiration date') }]}
>
<DatePicker style={{ width: '100%' }} showTime />
</Form.Item>
)}
{mode === 'edit' && (
<Form.Item name="enabled" label={$t('Enabled')} valuePropName="checked">
<Switch checkedChildren={$t('Yes')} unCheckedChildren={$t('No')} />
</Form.Item>
)}
</Form>
</Modal>
)
}
export default ApiKeyModal
@@ -1,67 +0,0 @@
import { $t } from '@common/locales'
import { DatePicker, Form, Input, Modal, Switch, theme } from 'antd'
import dayjs from 'dayjs'
import React from 'react'
interface EditKeyModalProps {
visible: boolean
onCancel: () => void
onSave: (values: any) => void
initialValues?: {
key: string
expirationDate: string
enabled: boolean
}
}
const EditKeyModal: React.FC<EditKeyModalProps> = ({ visible, onCancel, onSave, initialValues }) => {
const [form] = Form.useForm()
const { token } = theme.useToken()
const handleOk = async () => {
try {
const values = await form.validateFields()
onSave({
...values,
expirationDate: values.expirationDate.format('YYYY-MM-DD')
})
} catch (error) {
console.error('Validation failed:', error)
}
}
return (
<Modal title={$t('Edit API Key')} open={visible} onCancel={onCancel} onOk={handleOk} destroyOnClose>
<Form
form={form}
layout="vertical"
initialValues={{
...initialValues,
expirationDate: initialValues?.expirationDate ? dayjs(initialValues.expirationDate) : undefined
}}
>
<Form.Item
name="key"
label={$t('API Key')}
rules={[{ required: true, message: $t('Please input the API key') }]}
>
<Input.Password />
</Form.Item>
<Form.Item
name="expirationDate"
label={$t('Expiration Date')}
rules={[{ required: true, message: $t('Please select expiration date') }]}
>
<DatePicker style={{ width: '100%' }} />
</Form.Item>
<Form.Item name="enabled" label={$t('Enabled')} valuePropName="checked">
<Switch checkedChildren={$t('Yes')} unCheckedChildren={$t('No')} />
</Form.Item>
</Form>
</Modal>
)
}
export default EditKeyModal
@@ -7,7 +7,7 @@ import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { Badge, Button, message, Select, Space, Tooltip } from 'antd'
import React, { useEffect, useRef, useState } from 'react'
import EditKeyModal from './components/EditKeyModal'
import ApiKeyModal from './components/ApiKeyModal'
import StatusFilter from './components/StatusFilter'
interface APIKey {
@@ -23,7 +23,8 @@ const KeySettings: React.FC = () => {
const actionRef = useRef<ActionType>()
const [selectedProvider, setSelectedProvider] = useState<string>('openai')
const [statusFilter, setStatusFilter] = useState<string[]>([])
const [editModalVisible, setEditModalVisible] = useState(false)
const [modalVisible, setModalVisible] = useState(false)
const [modalMode, setModalMode] = useState<'add' | 'edit'>('add')
const [editingKey, setEditingKey] = useState<APIKey | null>(null)
const [apiKeys, setApiKeys] = useState<APIKey[]>([])
const { fetchData } = useFetch()
@@ -52,24 +53,46 @@ const KeySettings: React.FC = () => {
const handleEdit = (record: APIKey) => {
setEditingKey(record)
setEditModalVisible(true)
setModalMode('edit')
setModalVisible(true)
}
const handleAdd = () => {
setModalMode('add')
setModalVisible(true)
}
const handleModalCancel = () => {
setModalVisible(false)
setEditingKey(null)
}
const handleSave = (values: any) => {
if (editingKey) {
if (modalMode === 'edit' && editingKey) {
const newKeys = apiKeys.map((key) =>
key.id === editingKey.id
? {
...key,
key: values.key,
key: values.apiKey,
expirationDate: values.expirationDate,
status: values.enabled ? 'normal' : 'disabled'
}
: key
)
setApiKeys(newKeys)
} else {
// Handle add case
const newKey = {
id: String(Date.now()),
key: values.apiKey,
status: 'normal',
expirationDate: values.expirationDate,
priority: apiKeys.length + 1,
isDefault: apiKeys.length === 0
}
setApiKeys([...apiKeys, newKey])
}
setEditModalVisible(false)
setModalVisible(false)
setEditingKey(null)
}
@@ -145,7 +168,6 @@ const KeySettings: React.FC = () => {
}
]
// const filteredKeys = statusFilter.length > 0 ? apiKeys.filter((key) => statusFilter.includes(key.status)) : apiKeys
const filteredKeys = apiKeys
const beforeSearchNode = [
@@ -178,29 +200,27 @@ const KeySettings: React.FC = () => {
onDragSortEnd={handleDragSortEnd}
beforeSearchNode={beforeSearchNode}
addNewBtnTitle={$t('添加 API Key')}
onAddNewBtnClick={() => {
setEditingKey(null)
setEditModalVisible(true)
}}
onAddNewBtnClick={handleAdd}
rowKey="id"
/>
<EditKeyModal
visible={editModalVisible}
onCancel={() => {
setEditModalVisible(false)
setEditingKey(null)
}}
<ApiKeyModal
visible={modalVisible}
mode={modalMode}
onCancel={handleModalCancel}
onSave={handleSave}
vendorName={selectedProvider}
initialValues={
editingKey
? {
key: editingKey.key,
keyName: editingKey.key,
apiKey: editingKey.key,
expirationDate: editingKey.expirationDate,
enabled: editingKey.status !== 'disabled'
enabled: editingKey.status === 'normal'
}
: undefined
}
defaultKeyNumber={apiKeys.length + 1}
/>
</InsidePage>
)