diff --git a/web/app/(commonLayout)/apps/NewAppDialog.tsx b/web/app/(commonLayout)/apps/NewAppDialog.tsx deleted file mode 100644 index ff5b3bdd61..0000000000 --- a/web/app/(commonLayout)/apps/NewAppDialog.tsx +++ /dev/null @@ -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(null) - const [newAppMode, setNewAppMode] = useState() - const [isWithTemplate, setIsWithTemplate] = useState(false) - const [selectedTemplateIndex, setSelectedTemplateIndex] = useState(-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 && { - setEmoji({ icon, icon_background }) - setShowEmojiPicker(false) - }} - onClose={() => { - setEmoji({ icon: '🤖', icon_background: '#FFEAD5' }) - setShowEmojiPicker(false) - }} - />} - - - - - } - > -
-
-

{t('app.newApp.captionAppType')}

- {isWithTemplate && ( - <> - - setIsWithTemplate(false)} - > - {t('app.newApp.hideTemplates')} - - - )} -
- - {!isWithTemplate && ( - ( - <> -
    -
  • setNewAppMode('chat')} - > -
    - - - -
    -
    {t('app.newApp.chatApp')}
    -
    -
    {t('app.newApp.agentAssistant')}
    -
    -
    {t('app.newApp.chatAppIntro')}
    - {/* */} -
  • -
  • setNewAppMode('completion')} - > -
    - - {/* */} - - -
    -
    {t('app.newApp.completeApp')}
    -
    -
    -
    {t('app.newApp.completeAppIntro')}
    -
  • -
- - - ) - )} - - {isWithTemplate && ( -
    - {templates?.data?.map((template, index) => ( -
  • setSelectedTemplateIndex(index)} - > -
    - -
    -
    {template.name}
    -
    -
    -
    {template.model_config?.pre_prompt}
    -
    - -
    -
  • - ))} -
- )} - -
-

{t('app.newApp.captionName')}

-
- { setShowEmojiPicker(true) }} className='cursor-pointer' icon={emoji.icon} background={emoji.icon_background} /> - -
-
- - { - !isWithTemplate && ( -
- setIsWithTemplate(true)} - > - {t('app.newApp.showTemplates')} - -
- ) - } - -
- {isAppsFull && } -
- -} - -export default NewAppDialog diff --git a/web/app/components/app/create-app-dialog/appForm.tsx b/web/app/components/app/create-app-dialog/appForm.tsx index 8eda9186ff..f65415e95d 100644 --- a/web/app/components/app/create-app-dialog/appForm.tsx +++ b/web/app/components/app/create-app-dialog/appForm.tsx @@ -74,7 +74,7 @@ const AppForm = ({ }, [name, notify, t, appMode, emoji.icon, emoji.icon_background, description, onConfirm, onHide, mutateApps, router, isCurrentWorkspaceManager]) return ( - <> +
{/* app type */}
{t('app.newApp.captionAppType')}
@@ -162,7 +162,7 @@ const AppForm = ({
- +
) } diff --git a/web/app/components/app/create-app-dialog/index.tsx b/web/app/components/app/create-app-dialog/index.tsx index cc29a8ce3b..c65ae4a550 100644 --- a/web/app/components/app/create-app-dialog/index.tsx +++ b/web/app/components/app/create-app-dialog/index.tsx @@ -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 */} -
+
{/* Heading */} -
{t('app.newApp.startFromBlank')}
+
{t('app.newApp.startFromBlank')}
{/* app form */}
{/* template list */} -
-
{t('app.newApp.startFromTemplate')}
+
+
{t('app.newApp.startFromTemplate')}
+
-
OR
-
+
OR
+
diff --git a/web/app/components/explore/app-card/index.tsx b/web/app/components/explore/app-card/index.tsx index 14b96633b7..c97174030e 100644 --- a/web/app/components/explore/app-card/index.tsx +++ b/web/app/components/explore/app-card/index.tsx @@ -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 (
@@ -32,18 +34,24 @@ const AppCard = ({
{app.description}
- +
- { - canCreate && ( -
- -
- ) - } + {isExplore && canCreate && ( +
+ +
+ )} + {!isExplore && ( +
+ +
+ )}
) diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index 02a49782ec..005f0944b0 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -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 ( -
-
-
{t('explore.apps.title')}
-
{t('explore.apps.description')}
-
+
+ {pageType === PageType.EXPLORE && ( +
+
{t('explore.apps.title')}
+
{t('explore.apps.description')}
+
+ )} -
+
- {isShowCreateModal && ( = ({ }) => { 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 (
onChange('')} > {t('explore.apps.allCategories')} @@ -38,7 +40,6 @@ const Category: FC = ({
onChange(name)} > {categoryI18n[name] ? t(`explore.category.${name}`) : name} diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx index e6b049f31d..007ef0c14e 100644 --- a/web/app/components/explore/create-app-modal/index.tsx +++ b/web/app/components/explore/create-app-modal/index.tsx @@ -54,6 +54,7 @@ const CreateAppModal = ({ { }} + wrapperClassName='z-40' className={cn(s.modal, '!max-w-[480px]', 'px-8')} > diff --git a/web/app/components/header/app-nav/index.tsx b/web/app/components/header/app-nav/index.tsx index 3a90850a42..a3682cc752 100644 --- a/web/app/components/header/app-nav/index.tsx +++ b/web/app/components/header/app-nav/index.tsx @@ -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} /> - setShowNewAppDialog(false)} /> + setShowNewAppDialog(false)} + onSuccess={() => {}} + /> ) } diff --git a/web/app/components/header/app-selector/index.tsx b/web/app/components/header/app-selector/index.tsx index 49836a3e05..f9cc162433 100644 --- a/web/app/components/header/app-selector/index.tsx +++ b/web/app/components/header/app-selector/index.tsx @@ -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) { - setShowNewAppDialog(false)} /> + setShowNewAppDialog(false)} + onSuccess={() => {}} + />
) } diff --git a/web/i18n/en-US/app.ts b/web/i18n/en-US/app.ts index 73e3584f72..f8de26d4e5 100644 --- a/web/i18n/en-US/app.ts +++ b/web/i18n/en-US/app.ts @@ -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: diff --git a/web/i18n/zh-Hans/app.ts b/web/i18n/zh-Hans/app.ts index 2fbcd44b04..537b05e2d6 100644 --- a/web/i18n/zh-Hans/app.ts +++ b/web/i18n/zh-Hans/app.ts @@ -30,6 +30,7 @@ const translation = { appNamePlaceholder: '给你的应用起个名字', captionDescription: '描述', appDescriptionPlaceholder: '输入应用的描述', + useTemplate: '使用该模板', previewDemo: '预览 Demo', chatApp: '助手', chatAppIntro: