From a49600347c0fc617563286e04c86110830eec5e9 Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 25 Apr 2025 13:38:22 +0800 Subject: [PATCH 1/8] fix: update api rate limit on Pricing page --- web/app/components/billing/config.ts | 8 ++++++-- .../components/billing/pricing/plan-item.tsx | 19 ++++++++++++++----- web/app/components/billing/type.ts | 6 +++++- web/context/provider-context.tsx | 3 ++- web/i18n/en-US/billing.ts | 4 ++++ web/i18n/ja-JP/billing.ts | 10 +++++++--- web/i18n/zh-Hans/billing.ts | 4 ++++ 7 files changed, 42 insertions(+), 12 deletions(-) diff --git a/web/app/components/billing/config.ts b/web/app/components/billing/config.ts index 52651259ef..1d5fbc7491 100644 --- a/web/app/components/billing/config.ts +++ b/web/app/components/billing/config.ts @@ -1,3 +1,4 @@ +import type { BasicPlan } from '@/app/components/billing/type' import { Plan, type PlanInfo, Priority } from '@/app/components/billing/type' const supportModelProviders = 'OpenAI/Anthropic/Llama2/Azure OpenAI/Hugging Face/Replicate' @@ -10,7 +11,7 @@ export const contactSalesUrl = 'https://vikgc6bnu1s.typeform.com/dify-business' export const getStartedWithCommunityUrl = 'https://github.com/langgenius/dify' export const getWithPremiumUrl = 'https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6' -export const ALL_PLANS: Record = { +export const ALL_PLANS: Record = { sandbox: { level: 1, price: 0, @@ -22,6 +23,7 @@ export const ALL_PLANS: Record = { vectorSpace: '50MB', documentsUploadQuota: 0, documentsRequestQuota: 10, + apiRateLimit: 5000, documentProcessingPriority: Priority.standard, messageRequest: 200, annotatedResponse: 10, @@ -38,6 +40,7 @@ export const ALL_PLANS: Record = { vectorSpace: '5GB', documentsUploadQuota: 0, documentsRequestQuota: 100, + apiRateLimit: NUM_INFINITE, documentProcessingPriority: Priority.priority, messageRequest: 5000, annotatedResponse: 2000, @@ -54,6 +57,7 @@ export const ALL_PLANS: Record = { vectorSpace: '20GB', documentsUploadQuota: 0, documentsRequestQuota: 1000, + apiRateLimit: NUM_INFINITE, documentProcessingPriority: Priority.topPriority, messageRequest: 10000, annotatedResponse: 5000, @@ -62,7 +66,7 @@ export const ALL_PLANS: Record = { } export const defaultPlan = { - type: Plan.sandbox, + type: Plan.sandbox as BasicPlan, usage: { documents: 50, vectorSpace: 1, diff --git a/web/app/components/billing/pricing/plan-item.tsx b/web/app/components/billing/pricing/plan-item.tsx index a0b8685989..07af0ffec8 100644 --- a/web/app/components/billing/pricing/plan-item.tsx +++ b/web/app/components/billing/pricing/plan-item.tsx @@ -2,7 +2,8 @@ import type { FC, ReactNode } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiApps2Line, RiBook2Line, RiBrain2Line, RiChatAiLine, RiFileEditLine, RiFolder6Line, RiGroupLine, RiHardDrive3Line, RiHistoryLine, RiProgress3Line, RiQuestionLine, RiSeoLine } from '@remixicon/react' +import { RiApps2Line, RiBook2Line, RiBrain2Line, RiChatAiLine, RiFileEditLine, RiFolder6Line, RiGroupLine, RiHardDrive3Line, RiHistoryLine, RiProgress3Line, RiQuestionLine, RiSeoLine, RiTerminalBoxLine } from '@remixicon/react' +import type { BasicPlan } from '../type' import { Plan } from '../type' import { ALL_PLANS, NUM_INFINITE } from '../config' import Toast from '../../base/toast' @@ -15,8 +16,8 @@ import { useAppContext } from '@/context/app-context' import { fetchSubscriptionUrls } from '@/service/billing' type Props = { - currentPlan: Plan - plan: Plan + currentPlan: BasicPlan + plan: BasicPlan planRange: PlanRange canPay: boolean } @@ -127,8 +128,8 @@ const PlanItem: FC = ({
{style[plan].icon}
-
{t(`${i18nPrefix}.name`)}
- {isMostPopularPlan &&
+
{t(`${i18nPrefix}.name`)}
+ {isMostPopularPlan &&
@@ -205,6 +206,14 @@ const PlanItem: FC = ({ label={t('billing.plansCommon.documentsRequestQuota', { count: planInfo.documentsRequestQuota })} tooltip={t('billing.plansCommon.documentsRequestQuotaTooltip')} /> + } + label={ + planInfo.apiRateLimit === NUM_INFINITE ? `${t('billing.plansCommon.unlimitedApiRate')}` + : `${t('billing.plansCommon.apiRateLimitUnit', { count: planInfo.apiRateLimit })} ${t('billing.plansCommon.apiRateLimit')}` + } + tooltip={planInfo.apiRateLimit === NUM_INFINITE ? null : t('billing.plansCommon.apiRateLimitTooltip') as string} + /> } label={[t(`billing.plansCommon.priority.${planInfo.documentProcessingPriority}`), t('billing.plansCommon.documentProcessingPriority')].join('')} diff --git a/web/app/components/billing/type.ts b/web/app/components/billing/type.ts index 28bce37098..2f5728ceef 100644 --- a/web/app/components/billing/type.ts +++ b/web/app/components/billing/type.ts @@ -9,6 +9,9 @@ export enum Priority { priority = 'priority', topPriority = 'top-priority', } + +export type BasicPlan = Plan.sandbox | Plan.professional | Plan.team + export type PlanInfo = { level: number price: number @@ -20,6 +23,7 @@ export type PlanInfo = { vectorSpace: string documentsUploadQuota: number documentsRequestQuota: number + apiRateLimit: number documentProcessingPriority: Priority logHistory: number messageRequest: number @@ -60,7 +64,7 @@ export type CurrentPlanInfoBackend = { billing: { enabled: boolean subscription: { - plan: Plan + plan: BasicPlan } } members: { diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index bd997380e7..90af9aae0c 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -17,6 +17,7 @@ import { } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' +import type { BasicPlan } from '@/app/components/billing/type' import { Plan, type UsagePlanInfo } from '@/app/components/billing/type' import { fetchCurrentPlanInfo } from '@/service/billing' import { parseCurrentPlan } from '@/app/components/billing/utils' @@ -34,7 +35,7 @@ type ProviderContextState = { supportRetrievalMethods: RETRIEVE_METHOD[] isAPIKeySet: boolean plan: { - type: Plan + type: BasicPlan usage: UsagePlanInfo total: UsagePlanInfo } diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts index 893e730842..f3cd22d649 100644 --- a/web/i18n/en-US/billing.ts +++ b/web/i18n/en-US/billing.ts @@ -55,6 +55,10 @@ const translation = { vectorSpaceTooltip: 'Documents with the High Quality indexing mode will consume Knowledge Data Storage resources. When Knowledge Data Storage reaches the limit, new documents will not be uploaded.', documentsRequestQuota: '{{count,number}}/min Knowledge Request Rate Limit', documentsRequestQuotaTooltip: 'Specifies the total number of actions a workspace can perform per minute within the knowledge base, including dataset creation, deletion, updates, document uploads, modifications, archiving, and knowledge base queries. This metric is used to evaluate the performance of knowledge base requests. For example, if a Sandbox user performs 10 consecutive hit tests within one minute, their workspace will be temporarily restricted from performing the following actions for the next minute: dataset creation, deletion, updates, and document uploads or modifications. ', + apiRateLimit: 'API Rate Limit', + apiRateLimitUnit: '{{count,number}}/Day', + unlimitedApiRate: 'No API Rate Limit', + apiRateLimitTooltip: 'API Rate Limit applies to all requests made through the Dify API, including text generation, chat conversations, workflow executions, and document processing.', documentProcessingPriority: ' Document Processing', documentProcessingPriorityUpgrade: 'Process more data with higher accuracy at faster speeds.', priority: { diff --git a/web/i18n/ja-JP/billing.ts b/web/i18n/ja-JP/billing.ts index 891779d0b6..bcb509b85b 100644 --- a/web/i18n/ja-JP/billing.ts +++ b/web/i18n/ja-JP/billing.ts @@ -54,6 +54,10 @@ const translation = { vectorSpaceTooltip: '高品質インデックスモードのドキュメントは、知識データストレージのリソースを消費します。知識データストレージの上限に達すると、新しいドキュメントはアップロードされません。', documentsRequestQuota: '{{count,number}}/分のナレッジ リクエストのレート制限', documentsRequestQuotaTooltip: 'ナレッジベース内でワークスペースが1分間に実行できる操作の総数を示します。これには、データセットの作成、削除、更新、ドキュメントのアップロード、修正、アーカイブ、およびナレッジベースクエリが含まれます。この指標は、ナレッジベースリクエストのパフォーマンスを評価するために使用されます。例えば、Sandbox ユーザーが1分間に10回連続でヒットテストを実行した場合、そのワークスペースは次の1分間、データセットの作成、削除、更新、ドキュメントのアップロードや修正などの操作を一時的に実行できなくなります。', + apiRateLimit: 'APIレート制限', + apiRateLimitUnit: '{{count,number}}/日', + unlimitedApiRate: '無制限のAPIコール', + apiRateLimitTooltip: 'APIレート制限は、テキスト生成、チャットボット、ワークフロー、ドキュメント処理など、Dify API経由のすべてのリクエストに適用されます。', documentProcessingPriority: '文書処理', documentProcessingPriorityUpgrade: 'より高い精度と高速な速度でデータを処理します。', priority: { @@ -100,17 +104,17 @@ const translation = { }, plans: { sandbox: { - name: 'Sandbox(サンドボックス)', + name: 'Sandbox', for: '主要機能の無料体験', description: '主要機能を無料で体験', }, professional: { - name: 'Professional(プロフェッショナル)', + name: 'Professional', for: '個人開発者/小規模チーム向け', description: '個人開発者・小規模チームに最適', }, team: { - name: 'Team(チーム)', + name: 'Team', for: '中規模チーム向け', description: '成長期のチームに必要な機能を備えたプラン', }, diff --git a/web/i18n/zh-Hans/billing.ts b/web/i18n/zh-Hans/billing.ts index 8bddbfc2ba..e5fbff77b0 100644 --- a/web/i18n/zh-Hans/billing.ts +++ b/web/i18n/zh-Hans/billing.ts @@ -54,6 +54,10 @@ const translation = { vectorSpaceTooltip: '采用高质量索引模式的文档会消耗知识数据存储资源。当知识数据存储达到限制时,将不会上传新文档。', documentsRequestQuota: '{{count,number}}/分钟 知识库请求频率限制', documentsRequestQuotaTooltip: '指每分钟内,一个空间在知识库中可执行的操作总数,包括数据集的创建、删除、更新,文档的上传、修改、归档,以及知识库查询等,用于评估知识库请求的性能。例如,Sandbox 用户在 1 分钟内连续执行 10 次命中测试,其工作区将在接下来的 1 分钟内无法继续执行以下操作:数据集的创建、删除、更新,文档的上传、修改等操作。', + apiRateLimit: 'API 请求频率限制', + apiRateLimitUnit: '{{count,number}} 次/天', + unlimitedApiRate: 'API 请求频率无限制', + apiRateLimitTooltip: 'API 请求频率限制涵盖所有通过 Dify API 发起的调用,例如文本生成、聊天对话、工作流执行和文档处理等。', documentProcessingPriority: '文档处理', documentProcessingPriorityUpgrade: '以更快的速度、更高的精度处理更多的数据。', priority: { From dfa1a9076c3f2b6977103890d3aacedc85ad133f Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 25 Apr 2025 14:22:02 +0800 Subject: [PATCH 2/8] fix: character D in 'Day' to 'day' --- web/i18n/en-US/billing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts index f3cd22d649..57358dcf36 100644 --- a/web/i18n/en-US/billing.ts +++ b/web/i18n/en-US/billing.ts @@ -56,7 +56,7 @@ const translation = { documentsRequestQuota: '{{count,number}}/min Knowledge Request Rate Limit', documentsRequestQuotaTooltip: 'Specifies the total number of actions a workspace can perform per minute within the knowledge base, including dataset creation, deletion, updates, document uploads, modifications, archiving, and knowledge base queries. This metric is used to evaluate the performance of knowledge base requests. For example, if a Sandbox user performs 10 consecutive hit tests within one minute, their workspace will be temporarily restricted from performing the following actions for the next minute: dataset creation, deletion, updates, and document uploads or modifications. ', apiRateLimit: 'API Rate Limit', - apiRateLimitUnit: '{{count,number}}/Day', + apiRateLimitUnit: '{{count,number}}/day', unlimitedApiRate: 'No API Rate Limit', apiRateLimitTooltip: 'API Rate Limit applies to all requests made through the Dify API, including text generation, chat conversations, workflow executions, and document processing.', documentProcessingPriority: ' Document Processing', From bfe2d792956d29eccaaecf71db4860d12dd6d32b Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 25 Apr 2025 18:36:44 +0800 Subject: [PATCH 3/8] fix: dynamically generate the category menu on the create app page --- .../app/create-app-dialog/app-list/index.tsx | 2 +- .../create-app-dialog/app-list/sidebar.tsx | 58 +++++-------------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/web/app/components/app/create-app-dialog/app-list/index.tsx b/web/app/components/app/create-app-dialog/app-list/index.tsx index 702a07397d..8d88e8c361 100644 --- a/web/app/components/app/create-app-dialog/app-list/index.tsx +++ b/web/app/components/app/create-app-dialog/app-list/index.tsx @@ -191,7 +191,7 @@ const Apps = ({
{!searchKeywords &&
- { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} /> + { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} />
}
{searchFilteredList && searchFilteredList.length > 0 && <> diff --git a/web/app/components/app/create-app-dialog/app-list/sidebar.tsx b/web/app/components/app/create-app-dialog/app-list/sidebar.tsx index ee843675a5..86048316ab 100644 --- a/web/app/components/app/create-app-dialog/app-list/sidebar.tsx +++ b/web/app/components/app/create-app-dialog/app-list/sidebar.tsx @@ -1,26 +1,21 @@ 'use client' -import { RiAppsFill, RiChatSmileAiFill, RiExchange2Fill, RiPassPendingFill, RiQuillPenAiFill, RiSpeakAiFill, RiStickyNoteAddLine, RiTerminalBoxFill, RiThumbUpFill } from '@remixicon/react' +import { RiStickyNoteAddLine, RiThumbUpFill } from '@remixicon/react' import { useTranslation } from 'react-i18next' import classNames from '@/utils/classnames' import Divider from '@/app/components/base/divider' export enum AppCategories { RECOMMENDED = 'Recommended', - ASSISTANT = 'Assistant', - AGENT = 'Agent', - HR = 'HR', - PROGRAMMING = 'Programming', - WORKFLOW = 'Workflow', - WRITING = 'Writing', } type SidebarProps = { - current: AppCategories - onClick?: (category: AppCategories) => void + current: AppCategories | string + categories: string[] + onClick?: (category: AppCategories | string) => void onCreateFromBlank?: () => void } -export default function Sidebar({ current, onClick, onCreateFromBlank }: SidebarProps) { +export default function Sidebar({ current, categories, onClick, onCreateFromBlank }: SidebarProps) { const { t } = useTranslation() return
    @@ -28,12 +23,7 @@ export default function Sidebar({ current, onClick, onCreateFromBlank }: Sidebar
{t('app.newAppFromTemplate.byCategories')}
    - - - - - - + {categories.map(category => ())}
@@ -45,47 +35,25 @@ export default function Sidebar({ current, onClick, onCreateFromBlank }: Sidebar type CategoryItemProps = { active: boolean - category: AppCategories - onClick?: (category: AppCategories) => void + category: AppCategories | string + onClick?: (category: AppCategories | string) => void } function CategoryItem({ category, active, onClick }: CategoryItemProps) { return
  • { onClick?.(category) }}> -
    - -
    + {category === AppCategories.RECOMMENDED &&
    + +
    }
  • } type AppCategoryLabelProps = { - category: AppCategories + category: AppCategories | string className?: string } export function AppCategoryLabel({ category, className }: AppCategoryLabelProps) { - const { t } = useTranslation() - return {t(`app.newAppFromTemplate.sidebar.${category}`)} -} - -type AppCategoryIconProps = { - category: AppCategories -} -function AppCategoryIcon({ category }: AppCategoryIconProps) { - if (category === AppCategories.AGENT) - return - if (category === AppCategories.ASSISTANT) - return - if (category === AppCategories.HR) - return - if (category === AppCategories.PROGRAMMING) - return - if (category === AppCategories.RECOMMENDED) - return - if (category === AppCategories.WRITING) - return - if (category === AppCategories.WORKFLOW) - return - return + return {category} } From 9dfafc1382f31ce6793426a5f3cffc387126ee96 Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 25 Apr 2025 20:30:51 +0800 Subject: [PATCH 4/8] fix: update categories menu style on create from template modal --- .../components/app/create-app-dialog/app-list/sidebar.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/components/app/create-app-dialog/app-list/sidebar.tsx b/web/app/components/app/create-app-dialog/app-list/sidebar.tsx index 86048316ab..520c29dc3e 100644 --- a/web/app/components/app/create-app-dialog/app-list/sidebar.tsx +++ b/web/app/components/app/create-app-dialog/app-list/sidebar.tsx @@ -1,5 +1,5 @@ 'use client' -import { RiStickyNoteAddLine, RiThumbUpFill } from '@remixicon/react' +import { RiStickyNoteAddLine, RiThumbUpLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import classNames from '@/utils/classnames' import Divider from '@/app/components/base/divider' @@ -40,10 +40,10 @@ type CategoryItemProps = { } function CategoryItem({ category, active, onClick }: CategoryItemProps) { return
  • { onClick?.(category) }}> - {category === AppCategories.RECOMMENDED &&
    - + {category === AppCategories.RECOMMENDED &&
    +
    } From 7737a3108613ef781e3b1ad88a75ec69c3980a79 Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 25 Apr 2025 21:17:06 +0800 Subject: [PATCH 5/8] fix: update style --- .../components/app/create-app-dialog/app-list/index.tsx | 4 +++- .../components/app/create-app-dialog/app-list/sidebar.tsx | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/web/app/components/app/create-app-dialog/app-list/index.tsx b/web/app/components/app/create-app-dialog/app-list/index.tsx index 8d88e8c361..0b0b325d9a 100644 --- a/web/app/components/app/create-app-dialog/app-list/index.tsx +++ b/web/app/components/app/create-app-dialog/app-list/index.tsx @@ -198,7 +198,9 @@ const Apps = ({
    {searchKeywords ?

    {searchFilteredList.length > 1 ? t('app.newApp.foundResults', { count: searchFilteredList.length }) : t('app.newApp.foundResult', { count: searchFilteredList.length })}

    - : } + :
    + +
    }
    -
      +
      -
      {t('app.newAppFromTemplate.byCategories')}
      +
      {t('app.newAppFromTemplate.byCategories')}
        {categories.map(category => ())}
      @@ -55,5 +55,6 @@ type AppCategoryLabelProps = { className?: string } export function AppCategoryLabel({ category, className }: AppCategoryLabelProps) { - return {category} + const { t } = useTranslation() + return {category === AppCategories.RECOMMENDED ? t('app.newAppFromTemplate.sidebar.Recommended') : category} } From af2a3800453f39a709770c9ce5f7aa8d4642370f Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Sun, 27 Apr 2025 09:46:05 +0800 Subject: [PATCH 6/8] fix: check dsl version when create app from explore template --- .../dsl-confirm-modal.tsx | 46 +++++ web/app/components/explore/app-list/index.tsx | 85 +++++---- .../explore/create-app-modal/index.tsx | 4 +- web/hooks/use-import-dsl.ts | 163 ++++++++++++++++++ 4 files changed, 260 insertions(+), 38 deletions(-) create mode 100644 web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx create mode 100644 web/hooks/use-import-dsl.ts diff --git a/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx b/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx new file mode 100644 index 0000000000..e6aadaa326 --- /dev/null +++ b/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx @@ -0,0 +1,46 @@ +import { useTranslation } from 'react-i18next' +import Modal from '@/app/components/base/modal' +import Button from '@/app/components/base/button' + +type DSLConfirmModalProps = { + versions?: { + importedVersion: string + systemVersion: string + } + onCancel: () => void + onConfirm: () => void + confirmDisabled?: boolean +} +const DSLConfirmModal = ({ + versions = { importedVersion: '', systemVersion: '' }, + onCancel, + onConfirm, + confirmDisabled = false, +}: DSLConfirmModalProps) => { + const { t } = useTranslation() + + return ( + onCancel()} + className='w-[480px]' + > +
      +
      {t('app.newApp.appCreateDSLErrorTitle')}
      +
      +
      {t('app.newApp.appCreateDSLErrorPart1')}
      +
      {t('app.newApp.appCreateDSLErrorPart2')}
      +
      +
      {t('app.newApp.appCreateDSLErrorPart3')}{versions.importedVersion}
      +
      {t('app.newApp.appCreateDSLErrorPart4')}{versions.systemVersion}
      +
      +
      +
      + + +
      +
      + ) +} + +export default DSLConfirmModal diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index e217dda2b2..7e2d990bc8 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -1,12 +1,10 @@ 'use client' -import React, { useMemo, useState } from 'react' -import { useRouter } from 'next/navigation' +import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import useSWR from 'swr' import { useDebounceFn } from 'ahooks' -import Toast from '../../base/toast' import s from './style.module.css' import cn from '@/utils/classnames' import ExploreContext from '@/context/explore-context' @@ -14,17 +12,16 @@ import type { App } from '@/models/explore' import Category from '@/app/components/explore/category' import AppCard from '@/app/components/explore/app-card' import { fetchAppDetail, fetchAppList } from '@/service/explore' -import { importDSL } from '@/service/apps' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import CreateAppModal from '@/app/components/explore/create-app-modal' import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import Loading from '@/app/components/base/loading' -import { NEED_REFRESH_APP_LIST_KEY } from '@/config' -import { useAppContext } from '@/context/app-context' -import { getRedirection } from '@/utils/app-redirection' import Input from '@/app/components/base/input' -import { DSLImportMode } from '@/models/app' -import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' +import { + DSLImportMode, +} from '@/models/app' +import { useImportDSL } from '@/hooks/use-import-dsl' +import DSLConfirmModal from '@/app/components/app/create-from-dsl-modal/dsl-confirm-modal' type AppsProps = { onSuccess?: () => void @@ -39,8 +36,6 @@ const Apps = ({ onSuccess, }: AppsProps) => { const { t } = useTranslation() - const { isCurrentWorkspaceEditor } = useAppContext() - const { push } = useRouter() const { hasEditPermission } = useContext(ExploreContext) const allCategoriesEn = t('explore.apps.allCategories', { lng: 'en' }) @@ -115,7 +110,14 @@ const Apps = ({ const [currApp, setCurrApp] = React.useState(null) const [isShowCreateModal, setIsShowCreateModal] = React.useState(false) - const { handleCheckPluginDependencies } = usePluginDependencies() + + const { + handleImportDSL, + handleImportDSLConfirm, + versions, + isFetching, + } = useImportDSL() + const [showDSLConfirmModal, setShowDSLConfirmModal] = useState(false) const onCreate: CreateAppModalProps['onConfirm'] = async ({ name, icon_type, @@ -123,36 +125,34 @@ const Apps = ({ icon_background, description, }) => { - const { export_data, mode } = await fetchAppDetail( + const { export_data } = await fetchAppDetail( currApp?.app.id as string, ) - try { - const app = await importDSL({ - mode: DSLImportMode.YAML_CONTENT, - yaml_content: export_data, - name, - icon_type, - icon, - icon_background, - description, - }) - setIsShowCreateModal(false) - Toast.notify({ - type: 'success', - message: t('app.newApp.appCreated'), - }) - if (onSuccess) - onSuccess() - if (app.app_id) - await handleCheckPluginDependencies(app.app_id) - localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') - getRedirection(isCurrentWorkspaceEditor, { id: app.app_id!, mode }, push) - } - catch { - Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) + const payload = { + mode: DSLImportMode.YAML_CONTENT, + yaml_content: export_data, + name, + icon_type, + icon, + icon_background, + description, } + await handleImportDSL(payload, { + onSuccess: () => { + setIsShowCreateModal(false) + }, + onPending: () => { + setShowDSLConfirmModal(true) + }, + }) } + const onConfirmDSL = useCallback(async () => { + await handleImportDSLConfirm({ + onSuccess, + }) + }, [handleImportDSLConfirm, onSuccess]) + if (!categories || categories.length === 0) { return (
      @@ -225,9 +225,20 @@ const Apps = ({ appDescription={currApp?.app.description || ''} show={isShowCreateModal} onConfirm={onCreate} + confirmDisabled={isFetching} onHide={() => setIsShowCreateModal(false)} /> )} + { + showDSLConfirmModal && ( + setShowDSLConfirmModal(false)} + onConfirm={onConfirmDSL} + confirmDisabled={isFetching} + /> + ) + }
      ) } diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx index d6d521833a..f30b286786 100644 --- a/web/app/components/explore/create-app-modal/index.tsx +++ b/web/app/components/explore/create-app-modal/index.tsx @@ -35,6 +35,7 @@ export type CreateAppModalProps = { description: string use_icon_as_answer_icon?: boolean }) => Promise + confirmDisabled?: boolean onHide: () => void } @@ -50,6 +51,7 @@ const CreateAppModal = ({ appMode, appUseIconAsAnswerIcon, onConfirm, + confirmDisabled, onHide, }: CreateAppModalProps) => { const { t } = useTranslation() @@ -160,7 +162,7 @@ const CreateAppModal = ({