Commit 4edaa95c authored by JzoNg's avatar JzoNg

app menu

parent 067e6b5a
...@@ -31,19 +31,6 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => { ...@@ -31,19 +31,6 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const { appDetail, setAppDetail } = useStore() const { appDetail, setAppDetail } = useStore()
const { data: response } = useSWR(detailParams, fetchAppDetail) 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 navigation = useMemo(() => {
const navs = [ const navs = [
...(isCurrentWorkspaceManager ...(isCurrentWorkspaceManager
...@@ -97,7 +84,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => { ...@@ -97,7 +84,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return ( return (
<div className={cn(s.app, 'flex', 'overflow-hidden')}> <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"> <div className="bg-white grow overflow-hidden">
{children} {children}
</div> </div>
......
...@@ -122,6 +122,8 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -122,6 +122,8 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
message: t('app.newApp.appCreated'), message: t('app.newApp.appCreated'),
}) })
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
mutateApps()
onPlanInfoChanged()
getRedirection(isCurrentWorkspaceManager, newApp, push) getRedirection(isCurrentWorkspaceManager, newApp, push)
} }
catch (e) { catch (e) {
...@@ -175,13 +177,17 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -175,13 +177,17 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
<span className={s.actionName}>{t('common.operation.settings')}</span> <span className={s.actionName}>{t('common.operation.settings')}</span>
</button> </button>
<Divider className="!my-1" /> <Divider className="!my-1" />
<button className={s.actionItem} onClick={onClickDuplicate} disabled={detailState.loading}> {app.mode !== 'completion' && (
<span className={s.actionName}>{t('app.duplicate')}</span> <>
</button> <button className={s.actionItem} onClick={onClickDuplicate} disabled={detailState.loading}>
<button className={s.actionItem} onClick={onClickExport} disabled={detailState.loading}> <span className={s.actionName}>{t('app.duplicate')}</span>
<span className={s.actionName}>{t('app.export')}</span> </button>
</button> <button className={s.actionItem} onClick={onClickExport} disabled={detailState.loading}>
<Divider className="!my-1" /> <span className={s.actionName}>{t('app.export')}</span>
</button>
<Divider className="!my-1" />
</>
)}
<div <div
className={cn(s.actionItem, s.deleteActionItem, 'group')} className={cn(s.actionItem, s.deleteActionItem, 'group')}
onClick={onClickDelete} onClick={onClickDelete}
......
...@@ -49,11 +49,10 @@ const Apps = () => { ...@@ -49,11 +49,10 @@ const Apps = () => {
) )
const anchorRef = useRef<HTMLDivElement>(null) const anchorRef = useRef<HTMLDivElement>(null)
// #TODO# query key ???
const options = [ const options = [
{ value: 'all', text: t('app.types.all') }, { value: 'all', text: t('app.types.all') },
{ value: 'chat', text: t('app.types.chatbot') }, { 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') }, { value: 'workflow', text: t('app.types.workflow') },
] ]
......
This diff is collapsed.
...@@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react' ...@@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react'
import NavLink from './navLink' import NavLink from './navLink'
import type { NavIcon } from './navLink' import type { NavIcon } from './navLink'
import AppBasic from './basic' import AppBasic from './basic'
import AppInfo from './app-info'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { import {
AlignLeft01, AlignLeft01,
...@@ -62,14 +63,19 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf ...@@ -62,14 +63,19 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
${expand ? 'p-4' : 'p-2'} ${expand ? 'p-4' : 'p-2'}
`} `}
> >
<AppBasic {iconType === 'app' && (
mode={modeState} <AppInfo expand={expand}/>
iconType={iconType} )}
icon={icon} {iconType !== 'app' && (
icon_background={icon_background} <AppBasic
name={title} mode={modeState}
type={desc} iconType={iconType}
/> icon={icon}
icon_background={icon_background}
name={title}
type={desc}
/>
)}
</div> </div>
<nav <nav
className={` className={`
......
...@@ -149,7 +149,10 @@ const Apps = ({ ...@@ -149,7 +149,10 @@ const Apps = ({
</div> </div>
{isShowCreateModal && ( {isShowCreateModal && (
<CreateAppModal <CreateAppModal
appIcon={currApp?.app.icon || ''}
appIconBackground={currApp?.app.icon_background || ''}
appName={currApp?.app.name || ''} appName={currApp?.app.name || ''}
appDescription={currApp?.app.description || ''}
show={isShowCreateModal} show={isShowCreateModal}
onConfirm={onCreate} onConfirm={onCreate}
onHide={() => setIsShowCreateModal(false)} onHide={() => setIsShowCreateModal(false)}
......
...@@ -11,9 +11,12 @@ import AppsFull from '@/app/components/billing/apps-full-in-dialog' ...@@ -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' import { XClose } from '@/app/components/base/icons/src/vender/line/general'
export type CreateAppModalProps = { export type CreateAppModalProps = {
appName: string
appDescription?: string
show: boolean show: boolean
isEditModal?: boolean
appName: string
appDescription: string
appIcon: string
appIconBackground: string
onConfirm: (info: { onConfirm: (info: {
name: string name: string
icon: string icon: string
...@@ -24,9 +27,12 @@ export type CreateAppModalProps = { ...@@ -24,9 +27,12 @@ export type CreateAppModalProps = {
} }
const CreateAppModal = ({ const CreateAppModal = ({
show = false,
isEditModal = false,
appIcon,
appIconBackground,
appName, appName,
appDescription, appDescription,
show = false,
onConfirm, onConfirm,
onHide, onHide,
}: CreateAppModalProps) => { }: CreateAppModalProps) => {
...@@ -34,7 +40,7 @@ const CreateAppModal = ({ ...@@ -34,7 +40,7 @@ const CreateAppModal = ({
const [name, setName] = React.useState(appName) const [name, setName] = React.useState(appName)
const [showEmojiPicker, setShowEmojiPicker] = useState(false) 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 [description, setDescription] = useState(appDescription || '')
const { plan, enableBilling } = useProviderContext() const { plan, enableBilling } = useProviderContext()
...@@ -64,7 +70,12 @@ const CreateAppModal = ({ ...@@ -64,7 +70,12 @@ const CreateAppModal = ({
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onHide}> <div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onHide}>
<XClose className='w-4 h-4 text-gray-500' /> <XClose className='w-4 h-4 text-gray-500' />
</div> </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'> <div className='mb-9'>
{/* icon & name */} {/* icon & name */}
<div className='pt-2'> <div className='pt-2'>
...@@ -78,16 +89,6 @@ const CreateAppModal = ({ ...@@ -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' 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> </div>
{showEmojiPicker && <EmojiPicker
onSelect={(icon, icon_background) => {
setEmoji({ icon, icon_background })
setShowEmojiPicker(false)
}}
onClose={() => {
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
setShowEmojiPicker(false)
}}
/>}
</div> </div>
{/* description */} {/* description */}
<div className='pt-2'> <div className='pt-2'>
...@@ -99,10 +100,10 @@ const CreateAppModal = ({ ...@@ -99,10 +100,10 @@ const CreateAppModal = ({
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
/> />
</div> </div>
{isAppsFull && <AppsFull loc='app-explore-create' />} {!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />}
</div> </div>
<div className='flex flex-row-reverse'> <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> <Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button>
</div> </div>
</Modal> </Modal>
...@@ -112,7 +113,7 @@ const CreateAppModal = ({ ...@@ -112,7 +113,7 @@ const CreateAppModal = ({
setShowEmojiPicker(false) setShowEmojiPicker(false)
}} }}
onClose={() => { onClose={() => {
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' }) setEmoji({ icon: appIcon, icon_background: appIconBackground })
setShowEmojiPicker(false) setShowEmojiPicker(false)
}} }}
/>} />}
......
...@@ -58,9 +58,10 @@ const translation = { ...@@ -58,9 +58,10 @@ const translation = {
appCreated: 'App created', appCreated: 'App created',
appCreateFailed: 'Failed to create app', appCreateFailed: 'Failed to create app',
}, },
editApp: { editApp: 'Edit Info',
startToEdit: 'Edit App', editAppTitle: 'Edit App Info',
}, editDone: 'App info updated',
editFailed: 'Failed to update app info',
emoji: { emoji: {
ok: 'OK', ok: 'OK',
cancel: 'Cancel', cancel: 'Cancel',
......
...@@ -57,9 +57,10 @@ const translation = { ...@@ -57,9 +57,10 @@ const translation = {
appCreated: '应用已创建', appCreated: '应用已创建',
appCreateFailed: '应用创建失败', appCreateFailed: '应用创建失败',
}, },
editApp: { editApp: '编辑信息',
startToEdit: '编辑应用', editAppTitle: '编辑应用信息',
}, editDone: '应用信息已更新',
editFailed: '更新应用信息失败',
emoji: { emoji: {
ok: '确认', ok: '确认',
cancel: '取消', cancel: '取消',
......
import type { AppMode } from '@/types/app' import type { AppMode } from '@/types/app'
export type AppBasicInfo = { export type AppBasicInfo = {
id: string id: string
name: string
mode: AppMode mode: AppMode
icon: string icon: string
icon_background: string icon_background: string
is_agent: boolean name: string
description: string
} }
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant' export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant'
......
import type { Fetcher } from 'swr' 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 { 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 { CommonResponse } from '@/models/common'
import type { AppMode, ModelConfig } from '@/types/app' import type { AppMode, ModelConfig } from '@/types/app'
...@@ -20,6 +20,10 @@ export const createApp: Fetcher<AppDetailResponse, { name: string; icon: string; ...@@ -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 } }) 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 }) => { 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 } }) 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