mirror of https://github.com/langgenius/dify.git
app templates
This commit is contained in:
parent
14cfb310e3
commit
804a090457
|
|
@ -1,234 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import type { MouseEventHandler } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import classNames from 'classnames'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import style from '../list.module.css'
|
||||
import AppModeLabel from './AppModeLabel'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Dialog from '@/app/components/base/dialog'
|
||||
import type { AppMode } from '@/types/app'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { createApp, fetchAppTemplates } from '@/service/apps'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppsContext, { useAppContext } from '@/context/app-context'
|
||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
||||
import { AiText } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
|
||||
type NewAppDialogProps = {
|
||||
show: boolean
|
||||
onSuccess?: () => void
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
||||
const router = useRouter()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const nameInputRef = useRef<HTMLInputElement>(null)
|
||||
const [newAppMode, setNewAppMode] = useState<AppMode>()
|
||||
const [isWithTemplate, setIsWithTemplate] = useState(false)
|
||||
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState<number>(-1)
|
||||
|
||||
// Emoji Picker
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const [emoji, setEmoji] = useState({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||
|
||||
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
|
||||
|
||||
const { data: templates, mutate } = useSWR({ url: '/app-templates' }, fetchAppTemplates)
|
||||
const mutateTemplates = useCallback(
|
||||
() => mutate(),
|
||||
[],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
mutateTemplates()
|
||||
setIsWithTemplate(false)
|
||||
}
|
||||
}, [mutateTemplates, show])
|
||||
|
||||
const { plan, enableBilling } = useProviderContext()
|
||||
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
|
||||
|
||||
const isCreatingRef = useRef(false)
|
||||
const onCreate: MouseEventHandler = useCallback(async () => {
|
||||
const name = nameInputRef.current?.value
|
||||
if (!name) {
|
||||
notify({ type: 'error', message: t('app.newApp.nameNotEmpty') })
|
||||
return
|
||||
}
|
||||
if (!templates || (isWithTemplate && !(selectedTemplateIndex > -1))) {
|
||||
notify({ type: 'error', message: t('app.newApp.appTemplateNotSelected') })
|
||||
return
|
||||
}
|
||||
if (!isWithTemplate && !newAppMode) {
|
||||
notify({ type: 'error', message: t('app.newApp.appTypeRequired') })
|
||||
return
|
||||
}
|
||||
if (isCreatingRef.current)
|
||||
return
|
||||
isCreatingRef.current = true
|
||||
try {
|
||||
const app = await createApp({
|
||||
name,
|
||||
icon: emoji.icon,
|
||||
icon_background: emoji.icon_background,
|
||||
mode: isWithTemplate ? templates.data[selectedTemplateIndex].mode : newAppMode!,
|
||||
config: isWithTemplate ? templates.data[selectedTemplateIndex].model_config : undefined,
|
||||
})
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
if (onClose)
|
||||
onClose()
|
||||
notify({ type: 'success', message: t('app.newApp.appCreated') })
|
||||
mutateApps()
|
||||
router.push(`/app/${app.id}/${isCurrentWorkspaceManager ? 'configuration' : 'overview'}`)
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
isCreatingRef.current = false
|
||||
}, [isWithTemplate, newAppMode, notify, router, templates, selectedTemplateIndex, emoji])
|
||||
|
||||
return <>
|
||||
{showEmojiPicker && <EmojiPicker
|
||||
onSelect={(icon, icon_background) => {
|
||||
setEmoji({ icon, icon_background })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setEmoji({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
/>}
|
||||
<Dialog
|
||||
show={show}
|
||||
title={t('app.newApp.startToCreate')}
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button disabled={isAppsFull} type="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className='overflow-y-auto'>
|
||||
<div className={style.newItemCaption}>
|
||||
<h3 className='inline'>{t('app.newApp.captionAppType')}</h3>
|
||||
{isWithTemplate && (
|
||||
<>
|
||||
<span className='block ml-[9px] mr-[9px] w-[1px] h-[13px] bg-gray-200' />
|
||||
<span
|
||||
className='inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
|
||||
onClick={() => setIsWithTemplate(false)}
|
||||
>
|
||||
{t('app.newApp.hideTemplates')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isWithTemplate && (
|
||||
(
|
||||
<>
|
||||
<ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
|
||||
<li
|
||||
className={classNames(style.listItem, style.selectable, newAppMode === 'chat' && style.selected)}
|
||||
onClick={() => setNewAppMode('chat')}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
<span className={classNames(style.newItemIconImage, style.newItemIconChat)} />
|
||||
</span>
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{t('app.newApp.chatApp')}</div>
|
||||
</div>
|
||||
<div className='flex items-center h-[18px] border border-indigo-300 px-1 rounded-[5px] text-xs font-medium text-indigo-600 uppercase truncate'>{t('app.newApp.agentAssistant')}</div>
|
||||
</div>
|
||||
<div className={`${style.listItemDescription} ${style.noClip}`}>{t('app.newApp.chatAppIntro')}</div>
|
||||
{/* <div className={classNames(style.listItemFooter, 'justify-end')}>
|
||||
<a className={style.listItemLink} href='https://udify.app/chat/7CQBa5yyvYLSkZtx' target='_blank' rel='noopener noreferrer'>{t('app.newApp.previewDemo')}<span className={classNames(style.linkIcon, style.grayLinkIcon)} /></a>
|
||||
</div> */}
|
||||
</li>
|
||||
<li
|
||||
className={classNames(style.listItem, style.selectable, newAppMode === 'completion' && style.selected)}
|
||||
onClick={() => setNewAppMode('completion')}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
{/* <span className={classNames(style.newItemIconImage, style.newItemIconComplete)} /> */}
|
||||
<AiText className={classNames('w-5 h-5', newAppMode === 'completion' ? 'text-[#155EEF]' : 'text-gray-700')} />
|
||||
</span>
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{t('app.newApp.completeApp')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${style.listItemDescription} ${style.noClip}`}>{t('app.newApp.completeAppIntro')}</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</>
|
||||
)
|
||||
)}
|
||||
|
||||
{isWithTemplate && (
|
||||
<ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
|
||||
{templates?.data?.map((template, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={classNames(style.listItem, style.selectable, selectedTemplateIndex === index && style.selected)}
|
||||
onClick={() => setSelectedTemplateIndex(index)}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<AppIcon size='small' />
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{template.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{template.model_config?.pre_prompt}</div>
|
||||
<div className='inline-block pl-3.5'>
|
||||
<AppModeLabel mode={template.mode} isAgent={template.model_config.agent_mode.enabled} className='mt-2' />
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
<div className='mt-8'>
|
||||
<h3 className={style.newItemCaption}>{t('app.newApp.captionName')}</h3>
|
||||
<div className='flex items-center justify-between gap-3'>
|
||||
<AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} className='cursor-pointer' icon={emoji.icon} background={emoji.icon_background} />
|
||||
<input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('app.appNamePlaceholder') || ''} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
!isWithTemplate && (
|
||||
<div className='flex items-center h-[34px] mt-2'>
|
||||
<span
|
||||
className='inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
|
||||
onClick={() => setIsWithTemplate(true)}
|
||||
>
|
||||
{t('app.newApp.showTemplates')}<span className={style.rightIcon} />
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
{isAppsFull && <AppsFull loc='app-create' />}
|
||||
</Dialog>
|
||||
</>
|
||||
}
|
||||
|
||||
export default NewAppDialog
|
||||
|
|
@ -74,7 +74,7 @@ const AppForm = ({
|
|||
}, [name, notify, t, appMode, emoji.icon, emoji.icon_background, description, onConfirm, onHide, mutateApps, router, isCurrentWorkspaceManager])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='overflow-y-auto'>
|
||||
{/* app type */}
|
||||
<div className='pt-2 px-8'>
|
||||
<div className='py-2 text-sm leading-[20px] font-medium text-gray-900'>{t('app.newApp.captionAppType')}</div>
|
||||
|
|
@ -162,7 +162,7 @@ const AppForm = ({
|
|||
<Button className='mr-2 text-gray-700 text-sm font-medium' onClick={onHide}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button className='text-sm font-medium' disabled={isAppsFull || !name} type="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import NewAppDialog from './newAppDialog'
|
||||
import AppForm from './appForm'
|
||||
import AppList, { PageType } from '@/app/components/explore/app-list'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
type CreateAppDialogProps = {
|
||||
|
|
@ -21,18 +22,19 @@ const CreateAppDialog = ({ show, onSuccess, onClose }: CreateAppDialogProps) =>
|
|||
onClose={() => {}}
|
||||
>
|
||||
{/* blank form */}
|
||||
<div className='shrink-0 max-w-[480px] h-full bg-white overflow-y-auto'>
|
||||
<div className='shrink-0 flex flex-col max-w-[480px] h-full bg-white'>
|
||||
{/* Heading */}
|
||||
<div className='sticky top-0 pl-8 pr-6 pt-6 pb-3 rounded-ss-xl text-xl leading-[30px] font-semibold text-gray-900'>{t('app.newApp.startFromBlank')}</div>
|
||||
<div className='shrink-0 pl-8 pr-6 pt-6 pb-3 bg-white rounded-ss-xl text-xl leading-[30px] font-semibold text-gray-900 z-10'>{t('app.newApp.startFromBlank')}</div>
|
||||
{/* app form */}
|
||||
<AppForm onHide={onClose} onConfirm={onSuccess}/>
|
||||
</div>
|
||||
{/* template list */}
|
||||
<div className='grow bg-gray-100'>
|
||||
<div className='sticky top-0 pl-8 pr-6 pt-6 pb-3 rounded-se-xl text-xl leading-[30px] font-semibold text-gray-900'>{t('app.newApp.startFromTemplate')}</div>
|
||||
<div className='grow flex flex-col h-full bg-gray-100'>
|
||||
<div className='shrink-0 pl-8 pr-6 pt-6 pb-3 bg-gray-100 rounded-se-xl text-xl leading-[30px] font-semibold text-gray-900 z-10'>{t('app.newApp.startFromTemplate')}</div>
|
||||
<AppList pageType={PageType.CREATE} />
|
||||
</div>
|
||||
<div className='absolute top-6 left-[464px] w-8 h-8 p-1 bg-white border-2 border-gray-200 rounded-2xl text-xs leading-[20px] font-medium text-gray-500 cursor-default'>OR</div>
|
||||
<div className='absolute right-6 top-6 p-2 cursor-pointer' onClick={onClose}>
|
||||
<div className='absolute top-6 left-[464px] w-8 h-8 p-1 bg-white border-2 border-gray-200 rounded-2xl text-xs leading-[20px] font-medium text-gray-500 cursor-default z-20'>OR</div>
|
||||
<div className='absolute right-6 top-6 p-2 cursor-pointer z-20' onClick={onClose}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</NewAppDialog>
|
||||
|
|
|
|||
|
|
@ -12,15 +12,17 @@ export type AppCardProps = {
|
|||
app: App
|
||||
canCreate: boolean
|
||||
onCreate: () => void
|
||||
isExplore: boolean
|
||||
}
|
||||
|
||||
const AppCard = ({
|
||||
app,
|
||||
canCreate,
|
||||
onCreate,
|
||||
isExplore,
|
||||
}: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { app: appBasicInfo, is_agent } = app
|
||||
const { app: appBasicInfo } = app
|
||||
return (
|
||||
<div className={cn(s.wrap, 'col-span-1 bg-white border-2 border-solid border-transparent rounded-lg shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg')}>
|
||||
<div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
|
||||
|
|
@ -32,18 +34,24 @@ const AppCard = ({
|
|||
<div className='mb-3 px-[14px] h-9 text-xs leading-normal text-gray-500 line-clamp-2'>{app.description}</div>
|
||||
<div className='flex items-center flex-wrap min-h-[42px] px-[14px] pt-2 pb-[10px]'>
|
||||
<div className={s.mode}>
|
||||
<AppModeLabel mode={appBasicInfo.mode} isAgent={is_agent} />
|
||||
<AppModeLabel mode={appBasicInfo.mode} />
|
||||
</div>
|
||||
{
|
||||
canCreate && (
|
||||
<div className={cn(s.opWrap, 'flex items-center w-full space-x-2')}>
|
||||
<Button type='primary' className='grow flex items-center !h-7' onClick={() => onCreate()}>
|
||||
<PlusIcon className='w-4 h-4 mr-1' />
|
||||
<span className='text-xs'>{t('explore.appCard.addToWorkspace')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{isExplore && canCreate && (
|
||||
<div className={cn(s.opWrap, 'flex items-center w-full space-x-2')}>
|
||||
<Button type='primary' className='grow flex items-center !h-7' onClick={() => onCreate()}>
|
||||
<PlusIcon className='w-4 h-4 mr-1' />
|
||||
<span className='text-xs'>{t('explore.appCard.addToWorkspace')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{!isExplore && (
|
||||
<div className={cn(s.opWrap, 'flex items-center w-full space-x-2')}>
|
||||
<Button type='primary' className='grow flex items-center !h-7' onClick={() => onCreate()}>
|
||||
<PlusIcon className='w-4 h-4 mr-1' />
|
||||
<span className='text-xs'>{t('app.newApp.useTemplate')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
|
||||
import React, { useEffect } from 'react'
|
||||
import cn from 'classnames'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
|
|
@ -19,7 +20,18 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
|||
import { type AppMode } from '@/types/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
const Apps: FC = () => {
|
||||
type AppsProps = {
|
||||
pageType?: PageType
|
||||
}
|
||||
|
||||
export enum PageType {
|
||||
EXPLORE = 'explore',
|
||||
CREATE = 'create',
|
||||
}
|
||||
|
||||
const Apps = ({
|
||||
pageType = PageType.EXPLORE,
|
||||
}: AppsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const router = useRouter()
|
||||
|
|
@ -81,23 +93,36 @@ const Apps: FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='h-full flex flex-col border-l border-gray-200'>
|
||||
<div className='shrink-0 pt-6 px-12'>
|
||||
<div className={`mb-1 ${s.textGradient} text-xl font-semibold`}>{t('explore.apps.title')}</div>
|
||||
<div className='text-gray-500 text-sm'>{t('explore.apps.description')}</div>
|
||||
</div>
|
||||
<div className={cn(
|
||||
'flex flex-col border-l border-gray-200',
|
||||
pageType === PageType.EXPLORE ? 'h-full' : 'h-[calc(100%-76px)]',
|
||||
)}>
|
||||
{pageType === PageType.EXPLORE && (
|
||||
<div className='shrink-0 pt-6 px-12'>
|
||||
<div className={`mb-1 ${s.textGradient} text-xl font-semibold`}>{t('explore.apps.title')}</div>
|
||||
<div className='text-gray-500 text-sm'>{t('explore.apps.description')}</div>
|
||||
</div>
|
||||
)}
|
||||
<Category
|
||||
className='mt-6 px-12'
|
||||
className={cn(pageType === PageType.EXPLORE ? 'mt-6 px-12' : 'px-8 py-2')}
|
||||
list={categories}
|
||||
value={currCategory}
|
||||
onChange={setCurrCategory}
|
||||
/>
|
||||
<div className='relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'>
|
||||
<div className={cn(
|
||||
'relative flex flex-1 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow',
|
||||
pageType === PageType.EXPLORE ? 'mt-6' : 'mt-0 pt-2',
|
||||
)}>
|
||||
<nav
|
||||
className={`${s.appList} grid content-start gap-4 px-6 sm:px-12 shrink-0`}>
|
||||
className={cn(
|
||||
s.appList,
|
||||
'grid content-start shrink-0',
|
||||
pageType === PageType.EXPLORE ? 'gap-4 px-6 sm:px-12' : 'gap-3 px-8',
|
||||
)}>
|
||||
{currList.map(app => (
|
||||
<AppCard
|
||||
key={app.app_id}
|
||||
isExplore={pageType === PageType.EXPLORE}
|
||||
app={app}
|
||||
canCreate={hasEditPermission}
|
||||
onCreate={() => {
|
||||
|
|
@ -108,7 +133,6 @@ const Apps: FC = () => {
|
|||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{isShowCreateModal && (
|
||||
<CreateAppModal
|
||||
appName={currApp?.app.name || ''}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,15 @@ const Category: FC<ICategoryProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const itemClassName = (isSelected: boolean) => cn(isSelected ? 'bg-white text-primary-600 border-gray-200 font-semibold' : 'border-transparent font-medium', 'flex items-center h-7 px-3 border cursor-pointer rounded-lg')
|
||||
const itemStyle = (isSelected: boolean) => isSelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}
|
||||
const itemClassName = (isSelected: boolean) => cn(
|
||||
'px-3 py-[5px] h-[28px] rounded-lg border-[0.5px] border-transparent text-gray-700 font-medium leading-[18px] cursor-pointer hover:bg-gray-200',
|
||||
isSelected && 'bg-white border-gray-200 shadow-xs text-primary-600 hover:bg-white',
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'flex space-x-1 text-[13px] flex-wrap')}>
|
||||
<div
|
||||
className={itemClassName(value === '')}
|
||||
style={itemStyle(value === '')}
|
||||
onClick={() => onChange('')}
|
||||
>
|
||||
{t('explore.apps.allCategories')}
|
||||
|
|
@ -38,7 +40,6 @@ const Category: FC<ICategoryProps> = ({
|
|||
<div
|
||||
key={name}
|
||||
className={itemClassName(name === value)}
|
||||
style={itemStyle(name === value)}
|
||||
onClick={() => onChange(name)}
|
||||
>
|
||||
{categoryI18n[name] ? t(`explore.category.${name}`) : name}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ const CreateAppModal = ({
|
|||
<Modal
|
||||
isShow={show}
|
||||
onClose={() => { }}
|
||||
wrapperClassName='z-40'
|
||||
className={cn(s.modal, '!max-w-[480px]', 'px-8')}
|
||||
>
|
||||
<span className={s.close} onClick={onHide} />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { flatten } from 'lodash-es'
|
|||
import Nav from '../nav'
|
||||
import { Robot, RobotActive } from '../../base/icons/src/public/header-nav/studio'
|
||||
import { fetchAppDetail, fetchAppList } from '@/service/apps'
|
||||
import NewAppDialog from '@/app/(commonLayout)/apps/NewAppDialog'
|
||||
import CreateAppDialog from '@/app/components/app/create-app-dialog'
|
||||
import type { AppListResponse } from '@/models/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
|
|
@ -54,7 +54,11 @@ const AppNav = () => {
|
|||
onCreate={() => setShowNewAppDialog(true)}
|
||||
onLoadmore={handleLoadmore}
|
||||
/>
|
||||
<NewAppDialog show={showNewAppDialog} onClose={() => setShowNewAppDialog(false)} />
|
||||
<CreateAppDialog
|
||||
show={showNewAppDialog}
|
||||
onClose={() => setShowNewAppDialog(false)}
|
||||
onSuccess={() => {}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Menu, Transition } from '@headlessui/react'
|
|||
import { useRouter } from 'next/navigation'
|
||||
import Indicator from '../indicator'
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
import NewAppDialog from '@/app/(commonLayout)/apps/NewAppDialog'
|
||||
import CreateAppDialog from '@/app/components/app/create-app-dialog'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
|
|
@ -101,7 +101,11 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
|||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<NewAppDialog show={showNewAppDialog} onClose={() => setShowNewAppDialog(false)} />
|
||||
<CreateAppDialog
|
||||
show={showNewAppDialog}
|
||||
onClose={() => setShowNewAppDialog(false)}
|
||||
onSuccess={() => {}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const translation = {
|
|||
appNamePlaceholder: 'Give your app a name',
|
||||
captionDescription: 'Description',
|
||||
appDescriptionPlaceholder: 'Enter the description of the app',
|
||||
useTemplate: 'Use this template',
|
||||
previewDemo: 'Preview demo',
|
||||
chatApp: 'Assistant',
|
||||
chatAppIntro:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const translation = {
|
|||
appNamePlaceholder: '给你的应用起个名字',
|
||||
captionDescription: '描述',
|
||||
appDescriptionPlaceholder: '输入应用的描述',
|
||||
useTemplate: '使用该模板',
|
||||
previewDemo: '预览 Demo',
|
||||
chatApp: '助手',
|
||||
chatAppIntro:
|
||||
|
|
|
|||
Loading…
Reference in New Issue