diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx index 0af2e945f3..5fb3ff4b4d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx @@ -3,7 +3,7 @@ import Main from '@/app/components/app/log-annotation' import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type' export type IProps = { - params: { appId: string } + params: Promise<{ appId: string }> } const Logs = async () => { diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx index a4ee3922d9..415d82285c 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx @@ -3,12 +3,16 @@ import type { Locale } from '@/i18n' import DevelopMain from '@/app/components/develop' export type IDevelopProps = { - params: { locale: Locale; appId: string } + params: Promise<{ locale: Locale; appId: string }> } -const Develop = async ({ - params: { appId }, -}: IDevelopProps) => { +const Develop = async (props: IDevelopProps) => { + const params = await props.params + + const { + appId, + } = params + return } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx new file mode 100644 index 0000000000..1434a81b55 --- /dev/null +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx @@ -0,0 +1,177 @@ +'use client' +import type { FC } from 'react' +import { useUnmount } from 'ahooks' +import React, { useCallback, useEffect, useState } from 'react' +import { usePathname, useRouter } from 'next/navigation' +import { + RiDashboard2Fill, + RiDashboard2Line, + RiFileList3Fill, + RiFileList3Line, + RiTerminalBoxFill, + RiTerminalBoxLine, + RiTerminalWindowFill, + RiTerminalWindowLine, +} from '@remixicon/react' +import { useTranslation } from 'react-i18next' +import { useShallow } from 'zustand/react/shallow' +import { useContextSelector } from 'use-context-selector' +import s from './style.module.css' +import cn from '@/utils/classnames' +import { useStore } from '@/app/components/app/store' +import AppSideBar from '@/app/components/app-sidebar' +import type { NavIcon } from '@/app/components/app-sidebar/navLink' +import { fetchAppDetail, fetchAppSSO } from '@/service/apps' +import AppContext, { useAppContext } from '@/context/app-context' +import Loading from '@/app/components/base/loading' +import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' +import type { App } from '@/types/app' + +export type IAppDetailLayoutProps = { + children: React.ReactNode + appId: string +} + +const AppDetailLayout: FC = (props) => { + const { + children, + appId, // get appId in path + } = props + const { t } = useTranslation() + const router = useRouter() + const pathname = usePathname() + const media = useBreakpoints() + const isMobile = media === MediaType.mobile + const { isCurrentWorkspaceEditor, isLoadingCurrentWorkspace } = useAppContext() + const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore(useShallow(state => ({ + appDetail: state.appDetail, + setAppDetail: state.setAppDetail, + setAppSiderbarExpand: state.setAppSiderbarExpand, + }))) + const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false) + const [appDetailRes, setAppDetailRes] = useState(null) + const [navigation, setNavigation] = useState>([]) + const systemFeatures = useContextSelector(AppContext, state => state.systemFeatures) + + const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => { + const navs = [ + ...(isCurrentWorkspaceEditor + ? [{ + name: t('common.appMenus.promptEng'), + href: `/app/${appId}/${(mode === 'workflow' || mode === 'advanced-chat') ? 'workflow' : 'configuration'}`, + icon: RiTerminalWindowLine, + selectedIcon: RiTerminalWindowFill, + }] + : [] + ), + { + name: t('common.appMenus.apiAccess'), + href: `/app/${appId}/develop`, + icon: RiTerminalBoxLine, + selectedIcon: RiTerminalBoxFill, + }, + ...(isCurrentWorkspaceEditor + ? [{ + name: mode !== 'workflow' + ? t('common.appMenus.logAndAnn') + : t('common.appMenus.logs'), + href: `/app/${appId}/logs`, + icon: RiFileList3Line, + selectedIcon: RiFileList3Fill, + }] + : [] + ), + { + name: t('common.appMenus.overview'), + href: `/app/${appId}/overview`, + icon: RiDashboard2Line, + selectedIcon: RiDashboard2Fill, + }, + ] + return navs + }, []) + + useEffect(() => { + if (appDetail) { + document.title = `${(appDetail.name || 'App')} - Dify` + const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand' + const mode = isMobile ? 'collapse' : 'expand' + setAppSiderbarExpand(isMobile ? mode : localeMode) + // TODO: consider screen size and mode + // if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow')) + // setAppSiderbarExpand('collapse') + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [appDetail, isMobile]) + + useEffect(() => { + setAppDetail() + setIsLoadingAppDetail(true) + fetchAppDetail({ url: '/apps', id: appId }).then((res) => { + setAppDetailRes(res) + }).catch((e: any) => { + if (e.status === 404) + router.replace('/apps') + }).finally(() => { + setIsLoadingAppDetail(false) + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [appId, pathname]) + + useEffect(() => { + if (!appDetailRes || isLoadingCurrentWorkspace || isLoadingAppDetail) + return + const res = appDetailRes + // redirection + const canIEditApp = isCurrentWorkspaceEditor + if (!canIEditApp && (pathname.endsWith('configuration') || pathname.endsWith('workflow') || pathname.endsWith('logs'))) { + router.replace(`/app/${appId}/overview`) + return + } + if ((res.mode === 'workflow' || res.mode === 'advanced-chat') && (pathname).endsWith('configuration')) { + router.replace(`/app/${appId}/workflow`) + } + else if ((res.mode !== 'workflow' && res.mode !== 'advanced-chat') && (pathname).endsWith('workflow')) { + router.replace(`/app/${appId}/configuration`) + } + else { + setAppDetail({ ...res, enable_sso: false }) + setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode)) + if (systemFeatures.enable_web_sso_switch_component && canIEditApp) { + fetchAppSSO({ appId }).then((ssoRes) => { + setAppDetail({ ...res, enable_sso: ssoRes.enabled }) + }) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [appDetailRes, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, systemFeatures.enable_web_sso_switch_component]) + + useUnmount(() => { + setAppDetail() + }) + + if (!appDetail) { + return ( + + + + ) + } + + return ( + + {appDetail && ( + + )} + + {children} + + + ) +} +export default React.memo(AppDetailLayout) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx index e4a8db5b07..491a046e7d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx @@ -1,177 +1,14 @@ -'use client' -import type { FC } from 'react' -import { useUnmount } from 'ahooks' -import React, { useCallback, useEffect, useState } from 'react' -import { usePathname, useRouter } from 'next/navigation' -import { - RiDashboard2Fill, - RiDashboard2Line, - RiFileList3Fill, - RiFileList3Line, - RiTerminalBoxFill, - RiTerminalBoxLine, - RiTerminalWindowFill, - RiTerminalWindowLine, -} from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useShallow } from 'zustand/react/shallow' -import { useContextSelector } from 'use-context-selector' -import s from './style.module.css' -import cn from '@/utils/classnames' -import { useStore } from '@/app/components/app/store' -import AppSideBar from '@/app/components/app-sidebar' -import type { NavIcon } from '@/app/components/app-sidebar/navLink' -import { fetchAppDetail, fetchAppSSO } from '@/service/apps' -import AppContext, { useAppContext } from '@/context/app-context' -import Loading from '@/app/components/base/loading' -import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -import type { App } from '@/types/app' +import Main from './layout-main' -export type IAppDetailLayoutProps = { +const AppDetailLayout = async (props: { children: React.ReactNode - params: { appId: string } -} - -const AppDetailLayout: FC = (props) => { + params: Promise<{ appId: string }> +}) => { const { children, - params: { appId }, // get appId in path + params, } = props - const { t } = useTranslation() - const router = useRouter() - const pathname = usePathname() - const media = useBreakpoints() - const isMobile = media === MediaType.mobile - const { isCurrentWorkspaceEditor, isLoadingCurrentWorkspace } = useAppContext() - const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore(useShallow(state => ({ - appDetail: state.appDetail, - setAppDetail: state.setAppDetail, - setAppSiderbarExpand: state.setAppSiderbarExpand, - }))) - const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false) - const [appDetailRes, setAppDetailRes] = useState(null) - const [navigation, setNavigation] = useState>([]) - const systemFeatures = useContextSelector(AppContext, state => state.systemFeatures) - const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => { - const navs = [ - ...(isCurrentWorkspaceEditor - ? [{ - name: t('common.appMenus.promptEng'), - href: `/app/${appId}/${(mode === 'workflow' || mode === 'advanced-chat') ? 'workflow' : 'configuration'}`, - icon: RiTerminalWindowLine, - selectedIcon: RiTerminalWindowFill, - }] - : [] - ), - { - name: t('common.appMenus.apiAccess'), - href: `/app/${appId}/develop`, - icon: RiTerminalBoxLine, - selectedIcon: RiTerminalBoxFill, - }, - ...(isCurrentWorkspaceEditor - ? [{ - name: mode !== 'workflow' - ? t('common.appMenus.logAndAnn') - : t('common.appMenus.logs'), - href: `/app/${appId}/logs`, - icon: RiFileList3Line, - selectedIcon: RiFileList3Fill, - }] - : [] - ), - { - name: t('common.appMenus.overview'), - href: `/app/${appId}/overview`, - icon: RiDashboard2Line, - selectedIcon: RiDashboard2Fill, - }, - ] - return navs - }, []) - - useEffect(() => { - if (appDetail) { - document.title = `${(appDetail.name || 'App')} - Dify` - const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand' - const mode = isMobile ? 'collapse' : 'expand' - setAppSiderbarExpand(isMobile ? mode : localeMode) - // TODO: consider screen size and mode - // if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow')) - // setAppSiderbarExpand('collapse') - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appDetail, isMobile]) - - useEffect(() => { - setAppDetail() - setIsLoadingAppDetail(true) - fetchAppDetail({ url: '/apps', id: appId }).then((res) => { - setAppDetailRes(res) - }).catch((e: any) => { - if (e.status === 404) - router.replace('/apps') - }).finally(() => { - setIsLoadingAppDetail(false) - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appId, pathname]) - - useEffect(() => { - if (!appDetailRes || isLoadingCurrentWorkspace || isLoadingAppDetail) - return - const res = appDetailRes - // redirection - const canIEditApp = isCurrentWorkspaceEditor - if (!canIEditApp && (pathname.endsWith('configuration') || pathname.endsWith('workflow') || pathname.endsWith('logs'))) { - router.replace(`/app/${appId}/overview`) - return - } - if ((res.mode === 'workflow' || res.mode === 'advanced-chat') && (pathname).endsWith('configuration')) { - router.replace(`/app/${appId}/workflow`) - } - else if ((res.mode !== 'workflow' && res.mode !== 'advanced-chat') && (pathname).endsWith('workflow')) { - router.replace(`/app/${appId}/configuration`) - } - else { - setAppDetail({ ...res, enable_sso: false }) - setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode)) - if (systemFeatures.enable_web_sso_switch_component && canIEditApp) { - fetchAppSSO({ appId }).then((ssoRes) => { - setAppDetail({ ...res, enable_sso: ssoRes.enabled }) - }) - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appDetailRes, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, systemFeatures.enable_web_sso_switch_component]) - - useUnmount(() => { - setAppDetail() - }) - - if (!appDetail) { - return ( - - - - ) - } - - return ( - - {appDetail && ( - - )} - - {children} - - - ) + return {children} } -export default React.memo(AppDetailLayout) +export default AppDetailLayout diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx index 208078f612..79b45941f1 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx @@ -122,7 +122,7 @@ const CardView: FC = ({ appId, isInPanel, className }) => { return return ( - + - + {t('appOverview.analysis.title')} ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))} @@ -61,13 +61,13 @@ export default function ChartView({ appId }: IChartViewProps) { /> {!isWorkflow && ( - + )} {!isWorkflow && ( - + {isChatApp ? ( @@ -79,24 +79,24 @@ export default function ChartView({ appId }: IChartViewProps) { )} {!isWorkflow && ( - + )} {!isWorkflow && isChatApp && ( - + )} {isWorkflow && ( - + )} {isWorkflow && ( - + diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx index 47dd36eb81..0f1bb7e18d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx @@ -5,14 +5,18 @@ import TracingPanel from './tracing/panel' import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel' export type IDevelopProps = { - params: { appId: string } + params: Promise<{ appId: string }> } -const Overview = async ({ - params: { appId }, -}: IDevelopProps) => { +const Overview = async (props: IDevelopProps) => { + const params = await props.params + + const { + appId, + } = params + return ( - + diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx index ca6fa3a904..3d05575127 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx @@ -58,8 +58,8 @@ const ConfigBtn: FC = ({ }} > - - + + diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx index d7a2a615cb..20bf2137c2 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx @@ -162,15 +162,15 @@ const ConfigPopup: FC = ({ } return ( - - + + - {t(`${I18N_PREFIX}.tracing`)} + {t(`${I18N_PREFIX}.tracing`)} - + {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)} {!readOnly && ( @@ -189,7 +189,7 @@ const ConfigPopup: FC = ({ - + {t(`${I18N_PREFIX}.tracingDescription`)} @@ -211,7 +211,7 @@ const ConfigPopup: FC = ({ {configuredProviderPanel()} - {t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)} + {t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)} {moreProviderPanel()} diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx index 84c48a6dde..eecd356e08 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx @@ -26,7 +26,7 @@ const Field: FC = ({ return ( - {label} + {label} {isRequired && *} + {t('common.appMenus.overview')} ) @@ -143,7 +143,7 @@ const Panel: FC = () => { }, [setControlShowPopup]) if (!isLoaded) { return ( - + @@ -153,19 +153,19 @@ const Panel: FC = () => { } return ( - + {!inUseTracingProvider && ( <> - {t(`${I18N_PREFIX}.title`)} + {t(`${I18N_PREFIX}.title`)} e.stopPropagation()}> { /> - - + + > )} @@ -193,7 +193,7 @@ const Panel: FC = () => { <> - + {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)} diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx index 0800602924..a7675c4a66 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx @@ -167,11 +167,11 @@ const ProviderConfigModal: FC = ({ {!isShowRemoveConfirm ? ( - + - + - + {t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)} @@ -265,14 +265,14 @@ const ProviderConfigModal: FC = ({ )} - + {t(`${I18N_PREFIX}.viewDocsLink`, { key: t(`app.tracing.${type}.title`) })} - + {isEdit && ( @@ -305,11 +305,11 @@ const ProviderConfigModal: FC = ({ - - + + {t('common.modelProvider.encrypted.front')} diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx index 36221357a4..ec85ddd59c 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx @@ -65,36 +65,36 @@ const ProviderPanel: FC = ({ return ( - + - {isChosen && {t(`${I18N_PREFIX}.inUse`)}} + {isChosen && {t(`${I18N_PREFIX}.inUse`)}} {!readOnly && ( - + {hasConfigured && ( - - + + {t(`${I18N_PREFIX}.view`)} )} - + {t(`${I18N_PREFIX}.config`)} )} - + {t(`${I18N_PREFIX}.${type}.description`)} diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx index 0f51671b30..ec9117dd38 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx @@ -21,7 +21,7 @@ const TracingIcon: FC = ({ const sizeClass = sizeClassMap[size] return ( - + ) } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx index 1d26e6525a..f4d49425ae 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx @@ -4,7 +4,7 @@ import Workflow from '@/app/components/workflow' const Page = () => { return ( - + ) diff --git a/web/app/(commonLayout)/apps/AppCard.tsx b/web/app/(commonLayout)/apps/AppCard.tsx index 7de801c2c0..ff8165fc82 100644 --- a/web/app/(commonLayout)/apps/AppCard.tsx +++ b/web/app/(commonLayout)/apps/AppCard.tsx @@ -226,37 +226,37 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { } return ( - - {t('app.editApp')} + + {t('app.editApp')} - - {t('app.duplicate')} + + {t('app.duplicate')} - - {t('app.export')} + + {t('app.export')} {(app.mode === 'completion' || app.mode === 'chat') && ( <> - {t('app.switch')} + {t('app.switch')} > )} - - {t('app.openInExplore')} + + {t('app.openInExplore')} - + {t('common.operation.delete')} @@ -276,9 +276,9 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { e.preventDefault() getRedirection(isCurrentWorkspaceEditor, app, push) }} - className='relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg' + className='group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border-[1px] border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg' > - + { background={app.icon_background} imageUrl={app.icon_url} /> - + - - + + {app.name} - + {app.mode === 'advanced-chat' && {t('app.types.advanced').toUpperCase()}} {app.mode === 'chat' && {t('app.types.chatbot').toUpperCase()}} {app.mode === 'agent-chat' && {t('app.types.agent').toUpperCase()}} @@ -311,17 +311,17 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { {isCurrentWorkspaceEditor && ( <> - { + { e.stopPropagation() e.preventDefault() }}> { /> - - + + } position="br" trigger="click" btnElement={ - + } btnClassName={open => cn( open ? '!bg-black/5 !shadow-none' : '!bg-transparent', - 'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5', + 'h-8 w-8 rounded-md border-none !p-2 hover:!bg-black/5', ) } popupClassName={ @@ -359,7 +359,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ? '!w-[256px] translate-x-[-224px]' : '!w-[160px] translate-x-[-128px]' } - className={'h-fit !z-20'} + className={'!z-20 h-fit'} /> > diff --git a/web/app/(commonLayout)/apps/Apps.tsx b/web/app/(commonLayout)/apps/Apps.tsx index ec80f47299..4eca5012a1 100644 --- a/web/app/(commonLayout)/apps/Apps.tsx +++ b/web/app/(commonLayout)/apps/Apps.tsx @@ -78,10 +78,10 @@ const Apps = () => { const anchorRef = useRef(null) const options = [ - { value: 'all', text: t('app.types.all'), icon: }, - { value: 'chat', text: t('app.types.chatbot'), icon: }, - { value: 'agent-chat', text: t('app.types.agent'), icon: }, - { value: 'workflow', text: t('app.types.workflow'), icon: }, + { value: 'all', text: t('app.types.all'), icon: }, + { value: 'chat', text: t('app.types.chatbot'), icon: }, + { value: 'agent-chat', text: t('app.types.agent'), icon: }, + { value: 'workflow', text: t('app.types.workflow'), icon: }, ] useEffect(() => { @@ -134,7 +134,7 @@ const Apps = () => { return ( <> - + { {(data && data[0].total > 0) - ? + ? {isCurrentWorkspaceEditor && } {data.map(({ data: apps }) => apps.map(app => ( )))} - : + : {isCurrentWorkspaceEditor && } @@ -186,14 +186,14 @@ function NoAppsFound() { const { t } = useTranslation() function renderDefaultCard() { const defaultCards = Array.from({ length: 36 }, (_, index) => ( - + )) return defaultCards } return ( <> {renderDefaultCard()} - + {t('app.newApp.noAppsFound')} > diff --git a/web/app/(commonLayout)/apps/NewAppCard.tsx b/web/app/(commonLayout)/apps/NewAppCard.tsx index a90af4ea85..0b42577ee3 100644 --- a/web/app/(commonLayout)/apps/NewAppCard.tsx +++ b/web/app/(commonLayout)/apps/NewAppCard.tsx @@ -1,6 +1,6 @@ 'use client' -import { forwardRef, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { useRouter, useSearchParams, @@ -18,7 +18,15 @@ export type CreateAppCardProps = { onSuccess?: () => void } -const CreateAppCard = forwardRef(({ className, onSuccess }, ref) => { +const CreateAppCard = ( + { + ref, + className, + onSuccess, + }: CreateAppCardProps & { + ref: React.RefObject; + }, +) => { const { t } = useTranslation() const { onPlanInfoChanged } = useProviderContext() const searchParams = useSearchParams() @@ -39,22 +47,22 @@ const CreateAppCard = forwardRef(({ classNam return ( - - {t('app.createApp')} - setShowNewAppModal(true)}> - + + {t('app.createApp')} + setShowNewAppModal(true)}> + {t('app.newApp.startFromBlank')} - setShowNewAppTemplateDialog(true)}> - + setShowNewAppTemplateDialog(true)}> + {t('app.newApp.startFromTemplate')} setShowCreateFromDSLModal(true)} - className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover'> - + className='flex w-full cursor-pointer items-center rounded-lg px-6 py-[7px] text-[13px] font-medium leading-[18px] text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'> + {t('app.importDSL')} @@ -103,7 +111,7 @@ const CreateAppCard = forwardRef(({ classNam /> ) -}) +} CreateAppCard.displayName = 'CreateAppCard' export default CreateAppCard diff --git a/web/app/(commonLayout)/apps/page.tsx b/web/app/(commonLayout)/apps/page.tsx index 972aabc8bc..85fe433446 100644 --- a/web/app/(commonLayout)/apps/page.tsx +++ b/web/app/(commonLayout)/apps/page.tsx @@ -13,17 +13,17 @@ const AppList = () => { const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures) return ( - + - {systemFeatures.license.status === LicenseStatus.NONE && -} - -const DatasetDetailLayout: FC = (props) => { const { children, - params: { datasetId }, } = props - const pathname = usePathname() - const hideSideBar = /documents\/create$/.test(pathname) - const { t } = useTranslation() - const { isCurrentWorkspaceDatasetOperator } = useAppContext() - const media = useBreakpoints() - const isMobile = media === MediaType.mobile - - const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({ - url: 'fetchDatasetDetail', - datasetId, - }, apiParams => fetchDatasetDetail(apiParams.datasetId)) - - const { data: relatedApps } = useSWR({ - action: 'fetchDatasetRelatedApps', - datasetId, - }, apiParams => fetchDatasetRelatedApps(apiParams.datasetId)) - - const navigation = useMemo(() => { - const baseNavigation = [ - { name: t('common.datasetMenus.hitTesting'), href: `/datasets/${datasetId}/hitTesting`, icon: TargetIcon, selectedIcon: TargetSolidIcon }, - // { name: 'api & webhook', href: `/datasets/${datasetId}/api`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon }, - { name: t('common.datasetMenus.settings'), href: `/datasets/${datasetId}/settings`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon }, - ] - - if (datasetRes?.provider !== 'external') { - baseNavigation.unshift({ - name: t('common.datasetMenus.documents'), - href: `/datasets/${datasetId}/documents`, - icon: DocumentTextIcon, - selectedIcon: DocumentTextSolidIcon, - }) - } - return baseNavigation - }, [datasetRes?.provider, datasetId, t]) - - useEffect(() => { - if (datasetRes) - document.title = `${datasetRes.name || 'Dataset'} - Dify` - }, [datasetRes]) - - const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand) - - useEffect(() => { - const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand' - const mode = isMobile ? 'collapse' : 'expand' - setAppSiderbarExpand(isMobile ? mode : localeMode) - }, [isMobile, setAppSiderbarExpand]) - - if (!datasetRes && !error) - return - - return ( - - {!hideSideBar && : undefined} - iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'} - />} - mutateDatasetRes(), - }}> - {children} - - - ) + return {children} } -export default React.memo(DatasetDetailLayout) +export default DatasetDetailLayout diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx index 3a65f1d30f..d9a196d854 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx @@ -3,13 +3,13 @@ import { getLocaleOnServer, useTranslation as translate } from '@/i18n/server' import Form from '@/app/components/datasets/settings/form' const Settings = async () => { - const locale = getLocaleOnServer() + const locale = await getLocaleOnServer() const { t } = await translate(locale, 'dataset-settings') return ( - {t('title')} + {t('title')} {t('desc')} diff --git a/web/app/(commonLayout)/datasets/ApiServer.tsx b/web/app/(commonLayout)/datasets/ApiServer.tsx index 82d96c1987..0756178e7f 100644 --- a/web/app/(commonLayout)/datasets/ApiServer.tsx +++ b/web/app/(commonLayout)/datasets/ApiServer.tsx @@ -15,22 +15,22 @@ const ApiServer: FC = ({ const { t } = useTranslation() return ( - - - {t('appApi.apiServer')} - {apiBaseUrl} - + + + {t('appApi.apiServer')} + {apiBaseUrl} + - + {t('appApi.ok')} ) diff --git a/web/app/(commonLayout)/datasets/Container.tsx b/web/app/(commonLayout)/datasets/Container.tsx index f484d30a3d..c1d9950bf3 100644 --- a/web/app/(commonLayout)/datasets/Container.tsx +++ b/web/app/(commonLayout)/datasets/Container.tsx @@ -82,8 +82,8 @@ const Container = () => { }, [currentWorkspace, router]) return ( - - + + setActiveTab(newActiveTab)} @@ -108,13 +108,13 @@ const Container = () => { onChange={e => handleKeywordsChange(e.target.value)} onClear={() => handleKeywordsChange('')} /> - + setShowExternalApiPanel(true)} > - - {t('dataset.externalAPIPanelTitle')} + + {t('dataset.externalAPIPanelTitle')} )} diff --git a/web/app/(commonLayout)/datasets/DatasetCard.tsx b/web/app/(commonLayout)/datasets/DatasetCard.tsx index 016c9dcabb..c4dc2ed00d 100644 --- a/web/app/(commonLayout)/datasets/DatasetCard.tsx +++ b/web/app/(commonLayout)/datasets/DatasetCard.tsx @@ -84,17 +84,17 @@ const DatasetCard = ({ } return ( - - {t('common.operation.settings')} + + {t('common.operation.settings')} {props.showDelete && ( <> - + {t('common.operation.delete')} @@ -111,7 +111,7 @@ const DatasetCard = ({ return ( <> { e.preventDefault() @@ -121,25 +121,25 @@ const DatasetCard = ({ }} > {isExternalProvider(dataset.provider) && } - + - + - - - {dataset.name} + + + {dataset.name} {!dataset.embedding_available && ( - {t('dataset.unavailable')} + {t('dataset.unavailable')} )} - + : <> {dataset.document_count}{t('dataset.documentCount')} - · + · {Math.round(dataset.word_count / 1000)}{t('dataset.wordCount')} - · + · {dataset.app_count}{t('dataset.appCount')} > } @@ -162,7 +162,7 @@ const DatasetCard = ({ - { + { e.stopPropagation() e.preventDefault() }}> - - + + } position="br" trigger="click" btnElement={ - + } btnClassName={open => cn( open ? '!bg-black/5 !shadow-none' : '!bg-transparent', - 'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5', + 'h-8 w-8 rounded-md border-none !p-2 hover:!bg-black/5', ) } - className={'!w-[128px] h-fit !z-20'} + className={'!z-20 h-fit !w-[128px]'} /> diff --git a/web/app/(commonLayout)/datasets/DatasetFooter.tsx b/web/app/(commonLayout)/datasets/DatasetFooter.tsx index 179948dabe..1f297001e7 100644 --- a/web/app/(commonLayout)/datasets/DatasetFooter.tsx +++ b/web/app/(commonLayout)/datasets/DatasetFooter.tsx @@ -6,8 +6,8 @@ const DatasetFooter = () => { const { t } = useTranslation() return ( -