mirror of https://github.com/langgenius/dify.git
app menu
This commit is contained in:
parent
067e6b5ae7
commit
4edaa95cbf
|
|
@ -31,19 +31,6 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||
const { appDetail, setAppDetail } = useStore()
|
||||
const { data: response } = useSWR(detailParams, fetchAppDetail)
|
||||
|
||||
const appModeName = (() => {
|
||||
if (response?.mode === 'chat' || response?.mode === 'advanced-chat')
|
||||
return t('app.types.chatbot')
|
||||
|
||||
if (response?.mode === 'agent-chat')
|
||||
return t('app.types.agent')
|
||||
|
||||
if (response?.mode === 'completion')
|
||||
return t('app.types.completion')
|
||||
|
||||
return t('app.types.workflow')
|
||||
})()
|
||||
|
||||
const navigation = useMemo(() => {
|
||||
const navs = [
|
||||
...(isCurrentWorkspaceManager
|
||||
|
|
@ -97,7 +84,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||
|
||||
return (
|
||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||
<AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={appModeName} navigation={navigation} />
|
||||
<AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={response.mode} navigation={navigation} />
|
||||
<div className="bg-white grow overflow-hidden">
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
message: t('app.newApp.appCreated'),
|
||||
})
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
mutateApps()
|
||||
onPlanInfoChanged()
|
||||
getRedirection(isCurrentWorkspaceManager, newApp, push)
|
||||
}
|
||||
catch (e) {
|
||||
|
|
@ -175,13 +177,17 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
<span className={s.actionName}>{t('common.operation.settings')}</span>
|
||||
</button>
|
||||
<Divider className="!my-1" />
|
||||
<button className={s.actionItem} onClick={onClickDuplicate} disabled={detailState.loading}>
|
||||
<span className={s.actionName}>{t('app.duplicate')}</span>
|
||||
</button>
|
||||
<button className={s.actionItem} onClick={onClickExport} disabled={detailState.loading}>
|
||||
<span className={s.actionName}>{t('app.export')}</span>
|
||||
</button>
|
||||
<Divider className="!my-1" />
|
||||
{app.mode !== 'completion' && (
|
||||
<>
|
||||
<button className={s.actionItem} onClick={onClickDuplicate} disabled={detailState.loading}>
|
||||
<span className={s.actionName}>{t('app.duplicate')}</span>
|
||||
</button>
|
||||
<button className={s.actionItem} onClick={onClickExport} disabled={detailState.loading}>
|
||||
<span className={s.actionName}>{t('app.export')}</span>
|
||||
</button>
|
||||
<Divider className="!my-1" />
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
className={cn(s.actionItem, s.deleteActionItem, 'group')}
|
||||
onClick={onClickDelete}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,10 @@ const Apps = () => {
|
|||
)
|
||||
|
||||
const anchorRef = useRef<HTMLDivElement>(null)
|
||||
// #TODO# query key ???
|
||||
const options = [
|
||||
{ value: 'all', text: t('app.types.all') },
|
||||
{ value: 'chat', text: t('app.types.chatbot') },
|
||||
{ value: 'agent-chat', text: t('app.types.agent') },
|
||||
{ value: 'agent', text: t('app.types.agent') },
|
||||
{ value: 'workflow', text: t('app.types.workflow') },
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import cn from 'classnames'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import AppIcon from '../base/app-icon'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import AppsContext from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps'
|
||||
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
|
||||
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
|
||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
|
||||
export type IAppInfoProps = {
|
||||
expand: boolean
|
||||
}
|
||||
|
||||
const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { replace } = useRouter()
|
||||
const { onPlanInfoChanged } = useProviderContext()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const setAppDetail = useAppStore(state => state.setAppDetail)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
|
||||
const mutateApps = useContextSelector(
|
||||
AppsContext,
|
||||
state => state.mutateApps,
|
||||
)
|
||||
|
||||
const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({
|
||||
name,
|
||||
icon,
|
||||
icon_background,
|
||||
description,
|
||||
}) => {
|
||||
if (!appDetail)
|
||||
return
|
||||
try {
|
||||
const app = await updateAppInfo({
|
||||
appID: appDetail.id,
|
||||
name,
|
||||
icon,
|
||||
icon_background,
|
||||
description,
|
||||
})
|
||||
setShowEditModal(false)
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('app.editDone'),
|
||||
})
|
||||
console.log(app.description)
|
||||
setAppDetail(app)
|
||||
mutateApps()
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.editFailed') })
|
||||
}
|
||||
}, [appDetail, mutateApps, notify, setAppDetail, t])
|
||||
|
||||
const onCopy: DuplicateAppModalProps['onConfirm'] = async ({ name, icon, icon_background }) => {
|
||||
if (!appDetail)
|
||||
return
|
||||
try {
|
||||
const newApp = await copyApp({
|
||||
appID: appDetail.id,
|
||||
name,
|
||||
icon,
|
||||
icon_background,
|
||||
mode: appDetail.mode,
|
||||
})
|
||||
setShowDuplicateModal(false)
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('app.newApp.appCreated'),
|
||||
})
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
mutateApps()
|
||||
onPlanInfoChanged()
|
||||
getRedirection(true, newApp, replace)
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
}
|
||||
|
||||
const onExport = async () => {
|
||||
if (!appDetail)
|
||||
return
|
||||
try {
|
||||
const { data } = await exportAppConfig(appDetail.id)
|
||||
const a = document.createElement('a')
|
||||
const file = new Blob([data], { type: 'application/yaml' })
|
||||
a.href = URL.createObjectURL(file)
|
||||
a.download = `${appDetail.name}.yml`
|
||||
a.click()
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.exportFailed') })
|
||||
}
|
||||
}
|
||||
|
||||
const onConfirmDelete = useCallback(async () => {
|
||||
if (!appDetail)
|
||||
return
|
||||
try {
|
||||
await deleteApp(appDetail.id)
|
||||
notify({ type: 'success', message: t('app.appDeleted') })
|
||||
mutateApps()
|
||||
onPlanInfoChanged()
|
||||
replace('/apps')
|
||||
}
|
||||
catch (e: any) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: `${t('app.appDeleteFailed')}${'message' in e ? `: ${e.message}` : ''}`,
|
||||
})
|
||||
}
|
||||
setShowConfirmDelete(false)
|
||||
}, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, t])
|
||||
|
||||
if (!appDetail)
|
||||
return null
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-start'
|
||||
offset={4}
|
||||
>
|
||||
<div className='relative'>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => setOpen(v => !v)}
|
||||
className='block'
|
||||
>
|
||||
<div className='flex cursor-pointer'>
|
||||
<div className='shrink-0 mr-2'>
|
||||
<AppIcon icon={appDetail.icon} background={appDetail.icon_background} />
|
||||
</div>
|
||||
{expand && (
|
||||
<div className="grow w-0 pt-[2px]">
|
||||
<div className='flex justify-between items-center text-sm leading-4 font-medium text-gray-900'>
|
||||
<div className='truncate' title={appDetail.name}>{appDetail.name}</div>
|
||||
<ChevronDown className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />
|
||||
</div>
|
||||
<div className='flex items-center text-xs leading-[18px] font-medium text-gray-500 gap-1'>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.newApp.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1002]'>
|
||||
<div className='w-[320px] bg-white rounded-2xl shadow-xl'>
|
||||
{/* header */}
|
||||
<div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}>
|
||||
<div className='shrink-0 mr-2'>
|
||||
<AppIcon icon={appDetail.icon} background={appDetail.icon_background} />
|
||||
</div>
|
||||
<div className='grow w-0 pt-[2px]'>
|
||||
<div title={appDetail.name} className='flex justify-between items-center text-sm leading-4 font-medium text-gray-900 truncate'>{appDetail.name}</div>
|
||||
<div className='flex items-center text-xs leading-[18px] font-medium text-gray-500 gap-1'>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.newApp.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* desscription */}
|
||||
{appDetail.description && (
|
||||
<div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div>
|
||||
)}
|
||||
{/* operations */}
|
||||
<div></div>
|
||||
<Divider className="!my-1" />
|
||||
<div className="w-full py-1">
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
setOpen(false)
|
||||
setShowEditModal(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span>
|
||||
</div>
|
||||
{appDetail.mode !== 'completion' && (
|
||||
<>
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
setOpen(false)
|
||||
setShowDuplicateModal(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.duplicate')}</span>
|
||||
</div>
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={onExport}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.export')}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Divider className="!my-1" />
|
||||
<div className='group h-9 py-2 px-3 mx-1 flex items-center hover:bg-red-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
setOpen(false)
|
||||
setShowConfirmDelete(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5 group-hover:text-red-500'>
|
||||
{t('common.operation.delete')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
{showEditModal && (
|
||||
<CreateAppModal
|
||||
isEditModal
|
||||
appIcon={appDetail.icon}
|
||||
appIconBackground={appDetail.icon_background}
|
||||
appName={appDetail.name}
|
||||
appDescription={appDetail.description}
|
||||
show={showEditModal}
|
||||
onConfirm={onEdit}
|
||||
onHide={() => setShowEditModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showDuplicateModal && (
|
||||
<DuplicateAppModal
|
||||
appName={appDetail.name}
|
||||
icon={appDetail.icon}
|
||||
icon_background={appDetail.icon_background}
|
||||
show={showDuplicateModal}
|
||||
onConfirm={onCopy}
|
||||
onHide={() => setShowDuplicateModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={t('app.deleteAppConfirmTitle')}
|
||||
content={t('app.deleteAppConfirmContent')}
|
||||
isShow={showConfirmDelete}
|
||||
onClose={() => setShowConfirmDelete(false)}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(AppInfo)
|
||||
|
|
@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react'
|
|||
import NavLink from './navLink'
|
||||
import type { NavIcon } from './navLink'
|
||||
import AppBasic from './basic'
|
||||
import AppInfo from './app-info'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import {
|
||||
AlignLeft01,
|
||||
|
|
@ -62,14 +63,19 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
|
|||
${expand ? 'p-4' : 'p-2'}
|
||||
`}
|
||||
>
|
||||
<AppBasic
|
||||
mode={modeState}
|
||||
iconType={iconType}
|
||||
icon={icon}
|
||||
icon_background={icon_background}
|
||||
name={title}
|
||||
type={desc}
|
||||
/>
|
||||
{iconType === 'app' && (
|
||||
<AppInfo expand={expand}/>
|
||||
)}
|
||||
{iconType !== 'app' && (
|
||||
<AppBasic
|
||||
mode={modeState}
|
||||
iconType={iconType}
|
||||
icon={icon}
|
||||
icon_background={icon_background}
|
||||
name={title}
|
||||
type={desc}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<nav
|
||||
className={`
|
||||
|
|
|
|||
|
|
@ -149,7 +149,10 @@ const Apps = ({
|
|||
</div>
|
||||
{isShowCreateModal && (
|
||||
<CreateAppModal
|
||||
appIcon={currApp?.app.icon || ''}
|
||||
appIconBackground={currApp?.app.icon_background || ''}
|
||||
appName={currApp?.app.name || ''}
|
||||
appDescription={currApp?.app.description || ''}
|
||||
show={isShowCreateModal}
|
||||
onConfirm={onCreate}
|
||||
onHide={() => setIsShowCreateModal(false)}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@ import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
|||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
export type CreateAppModalProps = {
|
||||
appName: string
|
||||
appDescription?: string
|
||||
show: boolean
|
||||
isEditModal?: boolean
|
||||
appName: string
|
||||
appDescription: string
|
||||
appIcon: string
|
||||
appIconBackground: string
|
||||
onConfirm: (info: {
|
||||
name: string
|
||||
icon: string
|
||||
|
|
@ -24,9 +27,12 @@ export type CreateAppModalProps = {
|
|||
}
|
||||
|
||||
const CreateAppModal = ({
|
||||
show = false,
|
||||
isEditModal = false,
|
||||
appIcon,
|
||||
appIconBackground,
|
||||
appName,
|
||||
appDescription,
|
||||
show = false,
|
||||
onConfirm,
|
||||
onHide,
|
||||
}: CreateAppModalProps) => {
|
||||
|
|
@ -34,7 +40,7 @@ const CreateAppModal = ({
|
|||
|
||||
const [name, setName] = React.useState(appName)
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const [emoji, setEmoji] = useState({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||
const [emoji, setEmoji] = useState({ icon: appIcon, icon_background: appIconBackground })
|
||||
const [description, setDescription] = useState(appDescription || '')
|
||||
|
||||
const { plan, enableBilling } = useProviderContext()
|
||||
|
|
@ -64,7 +70,12 @@ const CreateAppModal = ({
|
|||
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onHide}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
<div className='mb-9 font-semibold text-xl leading-[30px] text-gray-900'>{t('explore.appCustomize.title', { name: appName })}</div>
|
||||
{isEditModal && (
|
||||
<div className='mb-9 font-semibold text-xl leading-[30px] text-gray-900'>{t('app.editAppTitle')}</div>
|
||||
)}
|
||||
{!isEditModal && (
|
||||
<div className='mb-9 font-semibold text-xl leading-[30px] text-gray-900'>{t('explore.appCustomize.title', { name: appName })}</div>
|
||||
)}
|
||||
<div className='mb-9'>
|
||||
{/* icon & name */}
|
||||
<div className='pt-2'>
|
||||
|
|
@ -78,16 +89,6 @@ const CreateAppModal = ({
|
|||
className='grow h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg border border-transparent outline-none appearance-none caret-primary-600 placeholder:text-gray-400 hover:bg-gray-50 hover:border hover:border-gray-300 focus:bg-gray-50 focus:border focus:border-gray-300 focus:shadow-xs'
|
||||
/>
|
||||
</div>
|
||||
{showEmojiPicker && <EmojiPicker
|
||||
onSelect={(icon, icon_background) => {
|
||||
setEmoji({ icon, icon_background })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
{/* description */}
|
||||
<div className='pt-2'>
|
||||
|
|
@ -99,10 +100,10 @@ const CreateAppModal = ({
|
|||
onChange={e => setDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{isAppsFull && <AppsFull loc='app-explore-create' />}
|
||||
{!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />}
|
||||
</div>
|
||||
<div className='flex flex-row-reverse'>
|
||||
<Button disabled={isAppsFull} className='w-24 ml-2' type='primary' onClick={submit}>{t('common.operation.create')}</Button>
|
||||
<Button disabled={!isEditModal && isAppsFull} className='w-24 ml-2' type='primary' onClick={submit}>{!isEditModal ? t('common.operation.create') : t('common.operation.save')}</Button>
|
||||
<Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
@ -112,7 +113,7 @@ const CreateAppModal = ({
|
|||
setShowEmojiPicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||
setEmoji({ icon: appIcon, icon_background: appIconBackground })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
/>}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,10 @@ const translation = {
|
|||
appCreated: 'App created',
|
||||
appCreateFailed: 'Failed to create app',
|
||||
},
|
||||
editApp: {
|
||||
startToEdit: 'Edit App',
|
||||
},
|
||||
editApp: 'Edit Info',
|
||||
editAppTitle: 'Edit App Info',
|
||||
editDone: 'App info updated',
|
||||
editFailed: 'Failed to update app info',
|
||||
emoji: {
|
||||
ok: 'OK',
|
||||
cancel: 'Cancel',
|
||||
|
|
|
|||
|
|
@ -57,9 +57,10 @@ const translation = {
|
|||
appCreated: '应用已创建',
|
||||
appCreateFailed: '应用创建失败',
|
||||
},
|
||||
editApp: {
|
||||
startToEdit: '编辑应用',
|
||||
},
|
||||
editApp: '编辑信息',
|
||||
editAppTitle: '编辑应用信息',
|
||||
editDone: '应用信息已更新',
|
||||
editFailed: '更新应用信息失败',
|
||||
emoji: {
|
||||
ok: '确认',
|
||||
cancel: '取消',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { AppMode } from '@/types/app'
|
||||
export type AppBasicInfo = {
|
||||
id: string
|
||||
name: string
|
||||
mode: AppMode
|
||||
icon: string
|
||||
icon_background: string
|
||||
is_agent: boolean
|
||||
name: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Fetcher } from 'swr'
|
||||
import { del, get, post } from './base'
|
||||
import { del, get, post, put } from './base'
|
||||
import type { ApikeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, GenerationIntroductionResponse, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse } from '@/models/app'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
import type { AppMode, ModelConfig } from '@/types/app'
|
||||
|
|
@ -20,6 +20,10 @@ export const createApp: Fetcher<AppDetailResponse, { name: string; icon: string;
|
|||
return post<AppDetailResponse>('apps', { body: { name, icon, icon_background, mode, description, model_config: config } })
|
||||
}
|
||||
|
||||
export const updateAppInfo: Fetcher<AppDetailResponse, { appID: string; name: string; icon: string; icon_background: string; description: string }> = ({ appID, name, icon, icon_background, description }) => {
|
||||
return put<AppDetailResponse>(`apps/${appID}`, { body: { name, icon, icon_background, description } })
|
||||
}
|
||||
|
||||
export const copyApp: Fetcher<AppDetailResponse, { appID: string; name: string; icon: string; icon_background: string; mode: AppMode; description?: string }> = ({ appID, name, icon, icon_background, mode, description }) => {
|
||||
return post<AppDetailResponse>(`apps/${appID}/copy`, { body: { name, icon, icon_background, mode, description } })
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue