Commit 4edaa95c authored by JzoNg's avatar JzoNg

app menu

parent 067e6b5a
......@@ -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') },
]
......
This diff is collapsed.
......@@ -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: '取消',
......
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'
......
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 } })
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment