Compare commits
10 Commits
main
...
chore/upgr
Author | SHA1 | Date | |
---|---|---|---|
![]() |
333aae6795 | ||
![]() |
e797e97b18 | ||
![]() |
4ad152f525 | ||
![]() |
58403b8238 | ||
![]() |
a465f092eb | ||
![]() |
f981eb4640 | ||
![]() |
25aaf53375 | ||
![]() |
5ec7920c42 | ||
![]() |
4c49e48465 | ||
![]() |
ed93954add |
@ -1,4 +1,4 @@
|
||||
x-shared-env: &shared-api-worker-env
|
||||
x-shared-env: &shared-api-worker-env
|
||||
services:
|
||||
# API service
|
||||
api:
|
||||
@ -57,6 +57,7 @@ services:
|
||||
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
||||
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
||||
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
||||
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
|
||||
|
||||
# The postgres database.
|
||||
db:
|
||||
|
@ -448,6 +448,7 @@ services:
|
||||
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
||||
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
||||
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
||||
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
|
||||
|
||||
# The postgres database.
|
||||
db:
|
||||
|
@ -28,3 +28,6 @@ NEXT_PUBLIC_CSP_WHITELIST=
|
||||
|
||||
# The maximum number of top-k value for RAG.
|
||||
NEXT_PUBLIC_TOP_K_MAX_VALUE=10
|
||||
|
||||
# The maximum number of tokens for segmentation
|
||||
NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000
|
||||
|
@ -2,10 +2,6 @@ import React from 'react'
|
||||
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 }
|
||||
}
|
||||
|
||||
const Logs = async () => {
|
||||
return (
|
||||
<Main pageType={PageType.annotation} />
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React from 'react'
|
||||
import { type Locale } from '@/i18n'
|
||||
import DevelopMain from '@/app/components/develop'
|
||||
|
||||
export type IDevelopProps = {
|
||||
params: { locale: Locale; appId: string }
|
||||
params: Promise<{ appId: string }>
|
||||
}
|
||||
|
||||
const Develop = async ({
|
||||
params: { appId },
|
||||
params,
|
||||
}: IDevelopProps) => {
|
||||
return <DevelopMain appId={appId} />
|
||||
return <DevelopMain appId={(await params).appId} />
|
||||
}
|
||||
|
||||
export default Develop
|
||||
|
@ -0,0 +1,174 @@
|
||||
'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<IAppDetailLayoutProps> = (props) => {
|
||||
const {
|
||||
children,
|
||||
appId,
|
||||
} = 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<App | null>(null)
|
||||
const [navigation, setNavigation] = useState<Array<{
|
||||
name: string
|
||||
href: string
|
||||
icon: NavIcon
|
||||
selectedIcon: NavIcon
|
||||
}>>([])
|
||||
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
|
||||
}, [t])
|
||||
|
||||
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')
|
||||
}
|
||||
}, [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)
|
||||
})
|
||||
}, [appId, router, setAppDetail])
|
||||
|
||||
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 })
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail, systemFeatures.enable_web_sso_switch_component])
|
||||
|
||||
useUnmount(() => {
|
||||
setAppDetail()
|
||||
})
|
||||
|
||||
if (!appDetail) {
|
||||
return (
|
||||
<div className='flex h-full items-center justify-center bg-background-body'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||
{appDetail && (
|
||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
||||
)}
|
||||
<div className="bg-components-panel-bg grow overflow-hidden">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(AppDetailLayout)
|
@ -1,174 +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<IAppDetailLayoutProps> = (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<App | null>(null)
|
||||
const [navigation, setNavigation] = useState<Array<{
|
||||
name: string
|
||||
href: string
|
||||
icon: NavIcon
|
||||
selectedIcon: NavIcon
|
||||
}>>([])
|
||||
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
|
||||
}, [t])
|
||||
|
||||
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')
|
||||
}
|
||||
}, [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)
|
||||
})
|
||||
}, [appId, router, setAppDetail])
|
||||
|
||||
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 })
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail, systemFeatures.enable_web_sso_switch_component])
|
||||
|
||||
useUnmount(() => {
|
||||
setAppDetail()
|
||||
})
|
||||
|
||||
if (!appDetail) {
|
||||
return (
|
||||
<div className='flex h-full items-center justify-center bg-background-body'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||
{appDetail && (
|
||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
||||
)}
|
||||
<div className="bg-components-panel-bg grow overflow-hidden">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return <Main appId={(await params).appId}>{children}</Main>
|
||||
}
|
||||
export default React.memo(AppDetailLayout)
|
||||
export default AppDetailLayout
|
||||
|
@ -5,12 +5,13 @@ 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 },
|
||||
params,
|
||||
}: IDevelopProps) => {
|
||||
const appId = (await params).appId
|
||||
return (
|
||||
<div className="h-full px-4 sm:px-16 py-6 overflow-scroll">
|
||||
<ApikeyInfoPanel />
|
||||
|
@ -2,14 +2,14 @@ import React from 'react'
|
||||
import MainDetail from '@/app/components/datasets/documents/detail'
|
||||
|
||||
export type IDocumentDetailProps = {
|
||||
params: { datasetId: string; documentId: string }
|
||||
params: Promise<{ datasetId: string; documentId: string }>
|
||||
}
|
||||
|
||||
const DocumentDetail = async ({
|
||||
params: { datasetId, documentId },
|
||||
params,
|
||||
}: IDocumentDetailProps) => {
|
||||
return (
|
||||
<MainDetail datasetId={datasetId} documentId={documentId} />
|
||||
<MainDetail datasetId={(await params).datasetId} documentId={(await params).documentId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import React from 'react'
|
||||
import Settings from '@/app/components/datasets/documents/detail/settings'
|
||||
|
||||
export type IProps = {
|
||||
params: { datasetId: string; documentId: string }
|
||||
params: Promise<{ datasetId: string; documentId: string }>
|
||||
}
|
||||
|
||||
const DocumentSettings = async ({
|
||||
params: { datasetId, documentId },
|
||||
params,
|
||||
}: IProps) => {
|
||||
return (
|
||||
<Settings datasetId={datasetId} documentId={documentId} />
|
||||
<Settings datasetId={(await params).datasetId} documentId={(await params).documentId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import React from 'react'
|
||||
import DatasetUpdateForm from '@/app/components/datasets/create'
|
||||
|
||||
export type IProps = {
|
||||
params: { datasetId: string }
|
||||
params: Promise<{ datasetId: string }>
|
||||
}
|
||||
|
||||
const Create = async ({
|
||||
params: { datasetId },
|
||||
params,
|
||||
}: IProps) => {
|
||||
return (
|
||||
<DatasetUpdateForm datasetId={datasetId} />
|
||||
<DatasetUpdateForm datasetId={(await params).datasetId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import React from 'react'
|
||||
import Main from '@/app/components/datasets/documents'
|
||||
|
||||
export type IProps = {
|
||||
params: { datasetId: string }
|
||||
params: Promise<{ datasetId: string }>
|
||||
}
|
||||
|
||||
const Documents = async ({
|
||||
params: { datasetId },
|
||||
params,
|
||||
}: IProps) => {
|
||||
return (
|
||||
<Main datasetId={datasetId} />
|
||||
<Main datasetId={(await params).datasetId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import React from 'react'
|
||||
import Main from '@/app/components/datasets/hit-testing'
|
||||
|
||||
type Props = {
|
||||
params: { datasetId: string }
|
||||
params: Promise<{ datasetId: string }>
|
||||
}
|
||||
|
||||
const HitTesting = ({
|
||||
params: { datasetId },
|
||||
const HitTesting = async ({
|
||||
params,
|
||||
}: Props) => {
|
||||
return (
|
||||
<Main datasetId={datasetId} />
|
||||
<Main datasetId={(await params).datasetId} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,228 @@
|
||||
'use client'
|
||||
import type { FC, SVGProps } from 'react'
|
||||
import React, { useEffect, useMemo } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
Cog8ToothIcon,
|
||||
DocumentTextIcon,
|
||||
PaperClipIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
import {
|
||||
Cog8ToothIcon as Cog8ToothSolidIcon,
|
||||
// CommandLineIcon as CommandLineSolidIcon,
|
||||
DocumentTextIcon as DocumentTextSolidIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import { RiApps2AddLine, RiInformation2Line } from '@remixicon/react'
|
||||
import s from './style.module.css'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
||||
import type { RelatedAppResponse } from '@/models/datasets'
|
||||
import AppSideBar from '@/app/components/app-sidebar'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useStore } from '@/app/components/app/store'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
|
||||
|
||||
export type IAppDetailLayoutProps = {
|
||||
children: React.ReactNode
|
||||
datasetId: string
|
||||
}
|
||||
|
||||
const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<g clipPath="url(#clip0_4610_6951)">
|
||||
<path d="M10.6666 5.33325V3.33325L12.6666 1.33325L13.3332 2.66659L14.6666 3.33325L12.6666 5.33325H10.6666ZM10.6666 5.33325L7.9999 7.99988M14.6666 7.99992C14.6666 11.6818 11.6818 14.6666 7.99992 14.6666C4.31802 14.6666 1.33325 11.6818 1.33325 7.99992C1.33325 4.31802 4.31802 1.33325 7.99992 1.33325M11.3333 7.99992C11.3333 9.84087 9.84087 11.3333 7.99992 11.3333C6.15897 11.3333 4.66659 9.84087 4.66659 7.99992C4.66659 6.15897 6.15897 4.66659 7.99992 4.66659" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4610_6951">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
}
|
||||
|
||||
const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M12.7733 0.67512C12.9848 0.709447 13.1669 0.843364 13.2627 1.03504L13.83 2.16961L14.9646 2.73689C15.1563 2.83273 15.2902 3.01486 15.3245 3.22639C15.3588 3.43792 15.2894 3.65305 15.1379 3.80458L13.1379 5.80458C13.0128 5.92961 12.8433 5.99985 12.6665 5.99985H10.9426L8.47124 8.47124C8.21089 8.73159 7.78878 8.73159 7.52843 8.47124C7.26808 8.21089 7.26808 7.78878 7.52843 7.52843L9.9998 5.05707V3.33318C9.9998 3.15637 10.07 2.9868 10.1951 2.86177L12.1951 0.861774C12.3466 0.710244 12.5617 0.640794 12.7733 0.67512Z" fill="#155EEF" />
|
||||
<path d="M1.99984 7.99984C1.99984 4.68613 4.68613 1.99984 7.99984 1.99984C8.36803 1.99984 8.6665 1.70136 8.6665 1.33317C8.6665 0.964981 8.36803 0.666504 7.99984 0.666504C3.94975 0.666504 0.666504 3.94975 0.666504 7.99984C0.666504 12.0499 3.94975 15.3332 7.99984 15.3332C12.0499 15.3332 15.3332 12.0499 15.3332 7.99984C15.3332 7.63165 15.0347 7.33317 14.6665 7.33317C14.2983 7.33317 13.9998 7.63165 13.9998 7.99984C13.9998 11.3135 11.3135 13.9998 7.99984 13.9998C4.68613 13.9998 1.99984 11.3135 1.99984 7.99984Z" fill="#155EEF" />
|
||||
<path d="M5.33317 7.99984C5.33317 6.52708 6.52708 5.33317 7.99984 5.33317C8.36803 5.33317 8.6665 5.03469 8.6665 4.6665C8.6665 4.29831 8.36803 3.99984 7.99984 3.99984C5.7907 3.99984 3.99984 5.7907 3.99984 7.99984C3.99984 10.209 5.7907 11.9998 7.99984 11.9998C10.209 11.9998 11.9998 10.209 11.9998 7.99984C11.9998 7.63165 11.7014 7.33317 11.3332 7.33317C10.965 7.33317 10.6665 7.63165 10.6665 7.99984C10.6665 9.4726 9.4726 10.6665 7.99984 10.6665C6.52708 10.6665 5.33317 9.4726 5.33317 7.99984Z" fill="#155EEF" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
|
||||
<path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
type IExtraInfoProps = {
|
||||
isMobile: boolean
|
||||
relatedApps?: RelatedAppResponse
|
||||
expand: boolean
|
||||
}
|
||||
|
||||
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
const locale = getLocaleOnClient()
|
||||
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
|
||||
const relatedAppsTotal = relatedApps?.data?.length || 0
|
||||
|
||||
useEffect(() => {
|
||||
setShowTips(!isMobile)
|
||||
}, [isMobile, setShowTips])
|
||||
|
||||
return <div>
|
||||
{hasRelatedApps && (
|
||||
<>
|
||||
{!isMobile && (
|
||||
<Tooltip
|
||||
position='right'
|
||||
noDecoration
|
||||
needsDelay
|
||||
popupContent={
|
||||
<LinkedAppsPanel
|
||||
relatedApps={relatedApps.data}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
|
||||
<span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
|
||||
<RiInformation2Line className='w-4 h-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
|
||||
{relatedAppsTotal || '--'}
|
||||
<PaperClipIcon className='h-4 w-4 text-gray-700' />
|
||||
</div>}
|
||||
</>
|
||||
)}
|
||||
{!hasRelatedApps && !expand && (
|
||||
<Tooltip
|
||||
position='right'
|
||||
noDecoration
|
||||
needsDelay
|
||||
popupContent={
|
||||
<div className='p-4 w-[240px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl'>
|
||||
<div className='inline-flex p-2 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle'>
|
||||
<RiApps2AddLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='text-xs text-text-tertiary my-2'>{t('common.datasetMenus.emptyTip')}</div>
|
||||
<a
|
||||
className='inline-flex items-center text-xs text-text-accent mt-2 cursor-pointer'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
<BookOpenIcon className='mr-1' />
|
||||
{t('common.datasetMenus.viewDoc')}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
|
||||
<span>{t('common.datasetMenus.noRelatedApp')}</span>
|
||||
<RiInformation2Line className='w-4 h-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||
const {
|
||||
children,
|
||||
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 <Loading type='app' />
|
||||
|
||||
return (
|
||||
<div className='grow flex overflow-hidden'>
|
||||
{!hideSideBar && <AppSideBar
|
||||
title={datasetRes?.name || '--'}
|
||||
icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
|
||||
icon_background={datasetRes?.icon_background || '#F5F5F5'}
|
||||
desc={datasetRes?.description || '--'}
|
||||
isExternal={datasetRes?.provider === 'external'}
|
||||
navigation={navigation}
|
||||
extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : undefined}
|
||||
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
|
||||
/>}
|
||||
<DatasetDetailContext.Provider value={{
|
||||
indexingTechnique: datasetRes?.indexing_technique,
|
||||
dataset: datasetRes,
|
||||
mutateDatasetRes: () => mutateDatasetRes(),
|
||||
}}>
|
||||
<div className="bg-background-default-subtle grow overflow-hidden">{children}</div>
|
||||
</DatasetDetailContext.Provider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(DatasetDetailLayout)
|
@ -1,228 +1,12 @@
|
||||
'use client'
|
||||
import type { FC, SVGProps } from 'react'
|
||||
import React, { useEffect, useMemo } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
Cog8ToothIcon,
|
||||
DocumentTextIcon,
|
||||
PaperClipIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
import {
|
||||
Cog8ToothIcon as Cog8ToothSolidIcon,
|
||||
// CommandLineIcon as CommandLineSolidIcon,
|
||||
DocumentTextIcon as DocumentTextSolidIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import { RiApps2AddLine, RiInformation2Line } from '@remixicon/react'
|
||||
import s from './style.module.css'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
||||
import type { RelatedAppResponse } from '@/models/datasets'
|
||||
import AppSideBar from '@/app/components/app-sidebar'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useStore } from '@/app/components/app/store'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
|
||||
import Main from './layout-main'
|
||||
|
||||
export type IAppDetailLayoutProps = {
|
||||
const DatasetDetailLayout = async ({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
params: { datasetId: string }
|
||||
params: Promise<{ datasetId: string }>
|
||||
}) => {
|
||||
return <Main datasetId={(await params).datasetId}>{children}</Main>
|
||||
}
|
||||
|
||||
const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<g clipPath="url(#clip0_4610_6951)">
|
||||
<path d="M10.6666 5.33325V3.33325L12.6666 1.33325L13.3332 2.66659L14.6666 3.33325L12.6666 5.33325H10.6666ZM10.6666 5.33325L7.9999 7.99988M14.6666 7.99992C14.6666 11.6818 11.6818 14.6666 7.99992 14.6666C4.31802 14.6666 1.33325 11.6818 1.33325 7.99992C1.33325 4.31802 4.31802 1.33325 7.99992 1.33325M11.3333 7.99992C11.3333 9.84087 9.84087 11.3333 7.99992 11.3333C6.15897 11.3333 4.66659 9.84087 4.66659 7.99992C4.66659 6.15897 6.15897 4.66659 7.99992 4.66659" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4610_6951">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
}
|
||||
|
||||
const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M12.7733 0.67512C12.9848 0.709447 13.1669 0.843364 13.2627 1.03504L13.83 2.16961L14.9646 2.73689C15.1563 2.83273 15.2902 3.01486 15.3245 3.22639C15.3588 3.43792 15.2894 3.65305 15.1379 3.80458L13.1379 5.80458C13.0128 5.92961 12.8433 5.99985 12.6665 5.99985H10.9426L8.47124 8.47124C8.21089 8.73159 7.78878 8.73159 7.52843 8.47124C7.26808 8.21089 7.26808 7.78878 7.52843 7.52843L9.9998 5.05707V3.33318C9.9998 3.15637 10.07 2.9868 10.1951 2.86177L12.1951 0.861774C12.3466 0.710244 12.5617 0.640794 12.7733 0.67512Z" fill="#155EEF" />
|
||||
<path d="M1.99984 7.99984C1.99984 4.68613 4.68613 1.99984 7.99984 1.99984C8.36803 1.99984 8.6665 1.70136 8.6665 1.33317C8.6665 0.964981 8.36803 0.666504 7.99984 0.666504C3.94975 0.666504 0.666504 3.94975 0.666504 7.99984C0.666504 12.0499 3.94975 15.3332 7.99984 15.3332C12.0499 15.3332 15.3332 12.0499 15.3332 7.99984C15.3332 7.63165 15.0347 7.33317 14.6665 7.33317C14.2983 7.33317 13.9998 7.63165 13.9998 7.99984C13.9998 11.3135 11.3135 13.9998 7.99984 13.9998C4.68613 13.9998 1.99984 11.3135 1.99984 7.99984Z" fill="#155EEF" />
|
||||
<path d="M5.33317 7.99984C5.33317 6.52708 6.52708 5.33317 7.99984 5.33317C8.36803 5.33317 8.6665 5.03469 8.6665 4.6665C8.6665 4.29831 8.36803 3.99984 7.99984 3.99984C5.7907 3.99984 3.99984 5.7907 3.99984 7.99984C3.99984 10.209 5.7907 11.9998 7.99984 11.9998C10.209 11.9998 11.9998 10.209 11.9998 7.99984C11.9998 7.63165 11.7014 7.33317 11.3332 7.33317C10.965 7.33317 10.6665 7.63165 10.6665 7.99984C10.6665 9.4726 9.4726 10.6665 7.99984 10.6665C6.52708 10.6665 5.33317 9.4726 5.33317 7.99984Z" fill="#155EEF" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
|
||||
<path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
type IExtraInfoProps = {
|
||||
isMobile: boolean
|
||||
relatedApps?: RelatedAppResponse
|
||||
expand: boolean
|
||||
}
|
||||
|
||||
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
const locale = getLocaleOnClient()
|
||||
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
|
||||
const relatedAppsTotal = relatedApps?.data?.length || 0
|
||||
|
||||
useEffect(() => {
|
||||
setShowTips(!isMobile)
|
||||
}, [isMobile, setShowTips])
|
||||
|
||||
return <div>
|
||||
{hasRelatedApps && (
|
||||
<>
|
||||
{!isMobile && (
|
||||
<Tooltip
|
||||
position='right'
|
||||
noDecoration
|
||||
needsDelay
|
||||
popupContent={
|
||||
<LinkedAppsPanel
|
||||
relatedApps={relatedApps.data}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
|
||||
<span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
|
||||
<RiInformation2Line className='w-4 h-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
|
||||
{relatedAppsTotal || '--'}
|
||||
<PaperClipIcon className='h-4 w-4 text-gray-700' />
|
||||
</div>}
|
||||
</>
|
||||
)}
|
||||
{!hasRelatedApps && !expand && (
|
||||
<Tooltip
|
||||
position='right'
|
||||
noDecoration
|
||||
needsDelay
|
||||
popupContent={
|
||||
<div className='p-4 w-[240px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl'>
|
||||
<div className='inline-flex p-2 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle'>
|
||||
<RiApps2AddLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='text-xs text-text-tertiary my-2'>{t('common.datasetMenus.emptyTip')}</div>
|
||||
<a
|
||||
className='inline-flex items-center text-xs text-text-accent mt-2 cursor-pointer'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
<BookOpenIcon className='mr-1' />
|
||||
{t('common.datasetMenus.viewDoc')}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
|
||||
<span>{t('common.datasetMenus.noRelatedApp')}</span>
|
||||
<RiInformation2Line className='w-4 h-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (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 <Loading type='app' />
|
||||
|
||||
return (
|
||||
<div className='grow flex overflow-hidden'>
|
||||
{!hideSideBar && <AppSideBar
|
||||
title={datasetRes?.name || '--'}
|
||||
icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
|
||||
icon_background={datasetRes?.icon_background || '#F5F5F5'}
|
||||
desc={datasetRes?.description || '--'}
|
||||
isExternal={datasetRes?.provider === 'external'}
|
||||
navigation={navigation}
|
||||
extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : undefined}
|
||||
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
|
||||
/>}
|
||||
<DatasetDetailContext.Provider value={{
|
||||
indexingTechnique: datasetRes?.indexing_technique,
|
||||
dataset: datasetRes,
|
||||
mutateDatasetRes: () => mutateDatasetRes(),
|
||||
}}>
|
||||
<div className="bg-background-default-subtle grow overflow-hidden">{children}</div>
|
||||
</DatasetDetailContext.Provider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(DatasetDetailLayout)
|
||||
export default DatasetDetailLayout
|
||||
|
@ -3,7 +3,7 @@ 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 (
|
||||
|
@ -3,14 +3,14 @@ import React from 'react'
|
||||
import Main from '@/app/components/explore/installed-app'
|
||||
|
||||
export type IInstalledAppProps = {
|
||||
params: {
|
||||
params: Promise<{
|
||||
appId: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
const InstalledApp: FC<IInstalledAppProps> = ({ params: { appId } }) => {
|
||||
const InstalledApp: FC<IInstalledAppProps> = async ({ params }) => {
|
||||
return (
|
||||
<Main id={appId} />
|
||||
<Main id={(await params).appId} />
|
||||
)
|
||||
}
|
||||
export default React.memo(InstalledApp)
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import { logout } from '@/service/common'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
@ -36,7 +36,7 @@ export default function AppSelector() {
|
||||
({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<Menu.Button
|
||||
<MenuButton
|
||||
className={`
|
||||
inline-flex items-center
|
||||
rounded-[20px] p-1x text-sm
|
||||
@ -46,7 +46,7 @@ export default function AppSelector() {
|
||||
`}
|
||||
>
|
||||
<Avatar name={userProfile.name} size={32} />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@ -57,14 +57,14 @@ export default function AppSelector() {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className="
|
||||
absolute -right-2 -top-1 w-60 max-w-80
|
||||
divide-y divide-divider-subtle origin-top-right rounded-lg bg-components-panel-bg-blur
|
||||
shadow-lg
|
||||
"
|
||||
>
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
<div className='p-1'>
|
||||
<div className='flex flex-nowrap items-center px-3 py-2'>
|
||||
<div className='grow'>
|
||||
@ -74,8 +74,8 @@ export default function AppSelector() {
|
||||
<Avatar name={userProfile.name} size={32} />
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<div className='p-1' onClick={() => handleLogout()}>
|
||||
<div
|
||||
className='flex items-center justify-start h-9 px-3 rounded-lg cursor-pointer group hover:bg-state-base-hover'
|
||||
@ -84,8 +84,8 @@ export default function AppSelector() {
|
||||
<div className='font-normal text-[14px] text-text-secondary'>{t('common.userProfile.logout')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import { useContext } from 'use-context-selector'
|
||||
import {
|
||||
useCSVDownloader,
|
||||
} from 'react-papaparse'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItems, Transition } from '@headlessui/react'
|
||||
import Button from '../../../base/button'
|
||||
import AddAnnotationModal from '../add-annotation-modal'
|
||||
import type { AnnotationItemBasic } from '../type'
|
||||
@ -87,11 +87,11 @@ const HeaderOptions: FC<Props> = ({
|
||||
<span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkImport')}</span>
|
||||
</button>
|
||||
<Menu as="div" className="relative w-full h-full">
|
||||
<Menu.Button className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
|
||||
<MenuButton className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
|
||||
<FileDownload02 className='w-4 h-4 text-text-tertiary' />
|
||||
<span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkExport')}</span>
|
||||
<ChevronRight className='shrink-0 w-[14px] h-[14px] text-text-tertiary' />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -101,7 +101,7 @@ const HeaderOptions: FC<Props> = ({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className={cn(
|
||||
'absolute top-[1px] left-1 -translate-x-full py-1 min-w-[100px] z-10 bg-components-panel-bg border-[0.5px] border-components-panel-on-panel-item-bg origin-top-right rounded-xl shadow-xs',
|
||||
)}
|
||||
@ -122,7 +122,7 @@ const HeaderOptions: FC<Props> = ({
|
||||
<button disabled={annotationUnavailable} className={cn('h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]', '!border-0')} onClick={JSONLOutput}>
|
||||
<span className='grow text-text-secondary system-sm-regular text-left'>JSONL</span>
|
||||
</button>
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Fragment, useCallback } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, Transition } from '@headlessui/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type DialogProps = {
|
||||
@ -20,7 +20,7 @@ const NewAppDialog = ({
|
||||
return (
|
||||
<Transition appear show={show} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-40" onClose={close}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@ -30,11 +30,13 @@ const NewAppDialog = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
|
||||
<div className="fixed inset-0">
|
||||
<div className="flex flex-col items-center justify-center min-h-full pt-[56px]">
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@ -43,10 +45,14 @@ const NewAppDialog = ({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={cn('grow relative w-full h-[calc(100vh-56px)] p-0 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-t-xl', className)}>
|
||||
<DialogPanel className={cn('grow relative w-full h-[calc(100vh-56px)] p-0 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-t-xl', className)}>
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</DialogPanel>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<DialogPanel className={cn('grow relative w-full h-[calc(100vh-56px)] p-0 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-t-xl', className)}>
|
||||
{children}
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Fragment, useCallback } from 'react'
|
||||
import type { ElementType, ReactNode } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, DialogTitle, Transition } from '@headlessui/react'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
// https://headlessui.com/react/dialog
|
||||
@ -34,7 +34,7 @@ const CustomDialog = ({
|
||||
return (
|
||||
<Transition appear show={show} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-40" onClose={close}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@ -44,11 +44,13 @@ const CustomDialog = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex items-center justify-center min-h-full">
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@ -57,14 +59,14 @@ const CustomDialog = ({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={classNames('w-full max-w-[800px] p-6 overflow-hidden transition-all transform bg-components-panel-bg border-[0.5px] border-components-panel-border shadow-xl rounded-2xl', className)}>
|
||||
<DialogPanel className={classNames('w-full max-w-[800px] p-6 overflow-hidden transition-all transform bg-components-panel-bg border-[0.5px] border-components-panel-border shadow-xl rounded-2xl', className)}>
|
||||
{Boolean(title) && (
|
||||
<Dialog.Title
|
||||
<DialogTitle
|
||||
as={titleAs || 'h3'}
|
||||
className={classNames('pr-8 pb-3 title-2xl-semi-bold text-text-primary', titleClassName)}
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
</DialogTitle>
|
||||
)}
|
||||
<div className={classNames(bodyClassName)}>
|
||||
{children}
|
||||
@ -74,8 +76,27 @@ const CustomDialog = ({
|
||||
{footer}
|
||||
</div>
|
||||
)}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</DialogPanel>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<DialogPanel className={classNames('w-full max-w-[800px] p-6 overflow-hidden transition-all transform bg-components-panel-bg border-[0.5px] border-components-panel-border shadow-xl rounded-2xl', className)}>
|
||||
{Boolean(title) && (
|
||||
<DialogTitle
|
||||
as={titleAs || 'h3'}
|
||||
className={classNames('pr-8 pb-3 title-2xl-semi-bold text-text-primary', titleClassName)}
|
||||
>
|
||||
{title}
|
||||
</DialogTitle>
|
||||
)}
|
||||
<div className={classNames(bodyClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
{Boolean(footer) && (
|
||||
<div className={classNames('flex items-center justify-end gap-2 px-6 pb-6 pt-3', footerClassName)}>
|
||||
{footer}
|
||||
</div>
|
||||
)}
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, DialogTitle } from '@headlessui/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/24/outline'
|
||||
import Button from '../button'
|
||||
@ -48,21 +48,21 @@ export default function Drawer({
|
||||
>
|
||||
<div className={cn('flex w-screen h-screen justify-end', positionCenter && '!justify-center')}>
|
||||
{/* mask */}
|
||||
<Dialog.Overlay
|
||||
<DialogPanel
|
||||
className={cn('z-40 fixed inset-0', mask && 'bg-black bg-opacity-30')}
|
||||
/>
|
||||
<div className={cn('relative z-50 flex flex-col justify-between bg-background-body w-full max-w-sm p-6 overflow-hidden text-left align-middle shadow-xl', panelClassname)}>
|
||||
<>
|
||||
{title && <Dialog.Title
|
||||
{title && <DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>}
|
||||
{showClose && <Dialog.Title className="flex items-center mb-4" as="div">
|
||||
</DialogTitle>}
|
||||
{showClose && <DialogTitle className="flex items-center mb-4" as="div">
|
||||
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
|
||||
</Dialog.Title>}
|
||||
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>{description}</Dialog.Description>}
|
||||
</DialogTitle>}
|
||||
{description && <div className='text-gray-500 text-xs font-normal mt-2'>{description}</div>}
|
||||
{children}
|
||||
</>
|
||||
{footer || (footer === null
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Fragment, useCallback } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, Transition } from '@headlessui/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type DialogProps = {
|
||||
@ -22,7 +22,7 @@ const DialogWrapper = ({
|
||||
return (
|
||||
<Transition appear show={show} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-40" onClose={close}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@ -32,11 +32,12 @@ const DialogWrapper = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
</Transition.Child> */}
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
|
||||
<div className="fixed inset-0">
|
||||
<div className={cn('flex flex-col items-end justify-center min-h-full pb-2', inWorkflow ? 'pt-[112px]' : 'pt-[64px] pr-2')}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@ -45,10 +46,14 @@ const DialogWrapper = ({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={cn('grow flex flex-col relative w-[420px] h-0 p-0 overflow-hidden text-left align-middle transition-all transform bg-components-panel-bg-alt border-components-panel-border shadow-xl', inWorkflow ? 'border-t-[0.5px] border-l-[0.5px] border-b-[0.5px] rounded-l-2xl' : 'border-[0.5px] rounded-2xl', className)}>
|
||||
<DialogPanel className={cn('grow flex flex-col relative w-[420px] h-0 p-0 overflow-hidden text-left align-middle transition-all transform bg-components-panel-bg-alt border-components-panel-border shadow-xl', inWorkflow ? 'border-t-[0.5px] border-l-[0.5px] border-b-[0.5px] rounded-l-2xl' : 'border-[0.5px] rounded-2xl', className)}>
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</DialogPanel>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<DialogPanel className={cn('grow flex flex-col relative w-[420px] h-0 p-0 overflow-hidden text-left align-middle transition-all transform bg-components-panel-bg-alt border-components-panel-border shadow-xl', inWorkflow ? 'border-t-[0.5px] border-l-[0.5px] border-b-[0.5px] rounded-l-2xl' : 'border-[0.5px] rounded-2xl', className)}>
|
||||
{children}
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -5,7 +5,7 @@ import React, { Fragment } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'
|
||||
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
@ -67,7 +67,7 @@ const VoiceParamConfig = ({
|
||||
<>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<div className='text-text-primary system-xl-semibold'>{t('appDebug.voice.voiceSettings.title')}</div>
|
||||
<div className='p-1 cursor-pointer' onClick={onClose}><RiCloseLine className='w-4 h-4 text-text-tertiary'/></div>
|
||||
<div className='p-1 cursor-pointer' onClick={onClose}><RiCloseLine className='w-4 h-4 text-text-tertiary' /></div>
|
||||
</div>
|
||||
<div className='mb-3'>
|
||||
<div className='mb-1 py-1 flex items-center text-text-secondary system-sm-semibold'>
|
||||
@ -92,7 +92,7 @@ const VoiceParamConfig = ({
|
||||
}}
|
||||
>
|
||||
<div className='relative h-8'>
|
||||
<Listbox.Button
|
||||
<ListboxButton
|
||||
className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}>
|
||||
<span className={classNames('block truncate text-left', !languageItem?.name && 'text-gray-400')}>
|
||||
{languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder}
|
||||
@ -103,7 +103,7 @@ const VoiceParamConfig = ({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
@ -111,10 +111,10 @@ const VoiceParamConfig = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
||||
<Listbox.Options
|
||||
<ListboxOptions
|
||||
className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
|
||||
{languages.map((item: Item) => (
|
||||
<Listbox.Option
|
||||
<ListboxOption
|
||||
key={item.value}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
|
||||
@ -133,14 +133,14 @@ const VoiceParamConfig = ({
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true"/>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
</ListboxOption>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
@ -160,7 +160,7 @@ const VoiceParamConfig = ({
|
||||
}}
|
||||
>
|
||||
<div className={'grow relative h-8'}>
|
||||
<Listbox.Button
|
||||
<ListboxButton
|
||||
className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}>
|
||||
<span
|
||||
className={classNames('block truncate text-left', !voiceItem?.name && 'text-gray-400')}>{voiceItem?.name ?? localVoicePlaceholder}</span>
|
||||
@ -170,7 +170,7 @@ const VoiceParamConfig = ({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
@ -178,10 +178,10 @@ const VoiceParamConfig = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
||||
<Listbox.Options
|
||||
<ListboxOptions
|
||||
className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
|
||||
{voiceItems?.map((item: Item) => (
|
||||
<Listbox.Option
|
||||
<ListboxOption
|
||||
key={item.value}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
|
||||
@ -199,14 +199,14 @@ const VoiceParamConfig = ({
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
|
||||
)}
|
||||
>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true"/>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
</ListboxOption>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, Transition } from '@headlessui/react'
|
||||
import { Fragment } from 'react'
|
||||
import { RiCloseLargeLine } from '@remixicon/react'
|
||||
import classNames from '@/utils/classnames'
|
||||
@ -27,7 +27,7 @@ export default function FullScreenModal({
|
||||
return (
|
||||
<Transition show={open} as={Fragment}>
|
||||
<Dialog as="div" className={classNames('modal-dialog', wrapperClassName)} onClose={onClose}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@ -37,7 +37,9 @@ export default function FullScreenModal({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-background-overlay-backdrop backdrop-blur-[6px]" />
|
||||
</Transition.Child>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<div className="fixed inset-0 bg-background-overlay-backdrop backdrop-blur-[6px]" />
|
||||
|
||||
<div
|
||||
className="fixed inset-0 h-screen w-screen p-4"
|
||||
@ -47,7 +49,7 @@ export default function FullScreenModal({
|
||||
}}
|
||||
>
|
||||
<div className="w-full h-full bg-background-default-subtle rounded-2xl border border-effects-highlight relative">
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@ -56,7 +58,7 @@ export default function FullScreenModal({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={classNames(
|
||||
<DialogPanel className={classNames(
|
||||
'h-full',
|
||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||
className,
|
||||
@ -72,8 +74,26 @@ export default function FullScreenModal({
|
||||
<RiCloseLargeLine className='w-3.5 h-3.5 text-components-button-tertiary-text' />
|
||||
</div>}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</DialogPanel>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<DialogPanel className={classNames(
|
||||
'h-full',
|
||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||
className,
|
||||
)}>
|
||||
{closable
|
||||
&& <div
|
||||
className='absolute z-50 top-3 right-3 w-9 h-9 flex items-center justify-center rounded-[10px]
|
||||
bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClose()
|
||||
}}>
|
||||
<RiCloseLargeLine className='w-3.5 h-3.5 text-components-button-tertiary-text' />
|
||||
</div>}
|
||||
{children}
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -18,13 +18,13 @@ export type IGAProps = {
|
||||
gaType: GaType
|
||||
}
|
||||
|
||||
const GA: FC<IGAProps> = ({
|
||||
const GA: FC<IGAProps> = async ({
|
||||
gaType,
|
||||
}) => {
|
||||
if (IS_CE_EDITION)
|
||||
return null
|
||||
|
||||
const nonce = process.env.NODE_ENV === 'production' ? headers().get('x-nonce') : ''
|
||||
const nonce = process.env.NODE_ENV === 'production' ? (await headers()).get('x-nonce') : ''
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Dialog, DialogPanel, DialogTitle, Transition } from '@headlessui/react'
|
||||
import { Fragment } from 'react'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import classNames from '@/utils/classnames'
|
||||
@ -30,7 +30,7 @@ export default function Modal({
|
||||
return (
|
||||
<Transition appear show={isShow} as={Fragment}>
|
||||
<Dialog as="div" className={classNames('relative z-50', wrapperClassName)} onClose={onClose}>
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@ -40,7 +40,9 @@ export default function Modal({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-background-overlay" />
|
||||
</Transition.Child>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<div className="fixed inset-0 bg-background-overlay" />
|
||||
|
||||
<div
|
||||
className="fixed inset-0 overflow-y-auto"
|
||||
@ -50,7 +52,7 @@ export default function Modal({
|
||||
}}
|
||||
>
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
{/* <Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@ -59,20 +61,20 @@ export default function Modal({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={classNames(
|
||||
<DialogPanel className={classNames(
|
||||
'w-full max-w-[480px] transform rounded-2xl bg-components-panel-bg p-6 text-left align-middle shadow-xl transition-all',
|
||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||
className,
|
||||
)}>
|
||||
{title && <Dialog.Title
|
||||
{title && <DialogTitle
|
||||
as="h3"
|
||||
className="title-2xl-semi-bold text-text-primary"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>}
|
||||
{description && <Dialog.Description className='text-text-secondary body-md-regular mt-2'>
|
||||
</DialogTitle>}
|
||||
{description && <div className='text-text-secondary body-md-regular mt-2'>
|
||||
{description}
|
||||
</Dialog.Description>}
|
||||
</div>}
|
||||
{closable
|
||||
&& <div className='absolute z-10 top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-state-base-hover'>
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' onClick={
|
||||
@ -83,8 +85,34 @@ export default function Modal({
|
||||
} />
|
||||
</div>}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</DialogPanel>
|
||||
</Transition.Child> */}
|
||||
{/* TODO: to new Transition */}
|
||||
<DialogPanel className={classNames(
|
||||
'w-full max-w-[480px] transform rounded-2xl bg-components-panel-bg p-6 text-left align-middle shadow-xl transition-all',
|
||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||
className,
|
||||
)}>
|
||||
{title && <DialogTitle
|
||||
as="h3"
|
||||
className="title-2xl-semi-bold text-text-primary"
|
||||
>
|
||||
{title}
|
||||
</DialogTitle>}
|
||||
{description && <div className='text-text-secondary body-md-regular mt-2'>
|
||||
{description}
|
||||
</div>}
|
||||
{closable
|
||||
&& <div className='absolute z-10 top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-state-base-hover'>
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' onClick={
|
||||
(e) => {
|
||||
e.stopPropagation()
|
||||
onClose()
|
||||
}
|
||||
} />
|
||||
</div>}
|
||||
{children}
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment } from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import NotionIcon from '../../notion-icon'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
@ -25,7 +25,7 @@ export default function WorkspaceSelector({
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={`flex items-center justify-center h-7 rounded-md hover:bg-gray-50 ${open && 'bg-gray-50'} cursor-pointer`}>
|
||||
<MenuButton className={`flex items-center justify-center h-7 rounded-md hover:bg-gray-50 ${open && 'bg-gray-50'} cursor-pointer`}>
|
||||
<NotionIcon
|
||||
className='ml-1 mr-2'
|
||||
src={currentWorkspace?.workspace_icon}
|
||||
@ -34,7 +34,7 @@ export default function WorkspaceSelector({
|
||||
<div className='mr-1 w-[90px] text-left text-sm font-medium text-gray-700 truncate' title={currentWorkspace?.workspace_name}>{currentWorkspace?.workspace_name}</div>
|
||||
<div className='mr-1 px-1 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-primary-600'>{currentWorkspace?.pages.length}</div>
|
||||
<div className={cn(s['down-arrow'], 'mr-2 w-3 h-3')} />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -44,7 +44,7 @@ export default function WorkspaceSelector({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className={cn(
|
||||
s.popup,
|
||||
`absolute left-0 top-8 w-80
|
||||
@ -55,7 +55,7 @@ export default function WorkspaceSelector({
|
||||
<div className="p-1 max-h-50 overflow-auto">
|
||||
{
|
||||
items.map(item => (
|
||||
<Menu.Item key={item.workspace_id}>
|
||||
<MenuItem key={item.workspace_id}>
|
||||
<div
|
||||
className='flex items-center px-3 h-9 hover:bg-gray-50 cursor-pointer'
|
||||
onClick={() => onSelect(item.workspace_id)}
|
||||
@ -70,11 +70,11 @@ export default function WorkspaceSelector({
|
||||
{item.pages.length} {t('common.dataSource.notion.selector.pageSelected')}
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'
|
||||
import { Fragment, cloneElement, useRef } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
@ -59,7 +59,7 @@ export default function CustomPopover({
|
||||
onMouseEnter: () => onMouseEnter(open),
|
||||
})}
|
||||
>
|
||||
<Popover.Button
|
||||
<PopoverButton
|
||||
ref={buttonRef}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
@ -70,9 +70,9 @@ export default function CustomPopover({
|
||||
)}
|
||||
>
|
||||
{btnElement}
|
||||
</Popover.Button>
|
||||
</PopoverButton>
|
||||
<Transition as={Fragment}>
|
||||
<Popover.Panel
|
||||
<PopoverPanel
|
||||
className={cn(
|
||||
'absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl',
|
||||
position === 'bottom' && '-translate-x-1/2 left-1/2',
|
||||
@ -109,7 +109,7 @@ export default function CustomPopover({
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</Popover.Panel>
|
||||
</PopoverPanel>
|
||||
</Transition>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { Fragment, useEffect, useState } from 'react'
|
||||
import { Combobox, Listbox, Transition } from '@headlessui/react'
|
||||
import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions, Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'
|
||||
import { ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import { RiCheckLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -100,7 +100,7 @@ const Select: FC<ISelectProps> = ({
|
||||
<div className={classNames('relative')}>
|
||||
<div className='group text-gray-800'>
|
||||
{allowSearch
|
||||
? <Combobox.Input
|
||||
? <ComboboxInput
|
||||
className={`w-full rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`}
|
||||
onChange={(event) => {
|
||||
if (!disabled)
|
||||
@ -108,28 +108,28 @@ const Select: FC<ISelectProps> = ({
|
||||
}}
|
||||
displayValue={(item: Item) => item?.name}
|
||||
/>
|
||||
: <Combobox.Button onClick={
|
||||
: <ComboboxButton onClick={
|
||||
() => {
|
||||
if (!disabled)
|
||||
setOpen(!open)
|
||||
}
|
||||
} className={classNames(`flex items-center h-9 w-full rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200`, optionClassName)}>
|
||||
<div className='w-0 grow text-left truncate' title={selectedItem?.name}>{selectedItem?.name}</div>
|
||||
</Combobox.Button>}
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none group-hover:bg-gray-200" onClick={
|
||||
</ComboboxButton>}
|
||||
<ComboboxButton className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none group-hover:bg-gray-200" onClick={
|
||||
() => {
|
||||
if (!disabled)
|
||||
setOpen(!open)
|
||||
}
|
||||
}>
|
||||
{open ? <ChevronUpIcon className="h-5 w-5" /> : <ChevronDownIcon className="h-5 w-5" />}
|
||||
</Combobox.Button>
|
||||
</ComboboxButton>
|
||||
</div>
|
||||
|
||||
{(filteredItems.length > 0 && open) && (
|
||||
<Combobox.Options className={`absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm ${overlayClassName}`}>
|
||||
<ComboboxOptions className={`absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm ${overlayClassName}`}>
|
||||
{filteredItems.map((item: Item) => (
|
||||
<Combobox.Option
|
||||
<ComboboxOption
|
||||
key={item.value}
|
||||
value={item}
|
||||
className={({ active }: { active: boolean }) =>
|
||||
@ -160,9 +160,9 @@ const Select: FC<ISelectProps> = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Combobox.Option>
|
||||
</ComboboxOption>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</ComboboxOptions>
|
||||
)}
|
||||
</div>
|
||||
</Combobox >
|
||||
@ -209,9 +209,9 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
}}
|
||||
>
|
||||
<div className={classNames('group/simple-select relative h-9', wrapperClassName)}>
|
||||
{renderTrigger && <Listbox.Button className='w-full'>{renderTrigger(selectedItem)}</Listbox.Button>}
|
||||
{renderTrigger && <ListboxButton className='w-full'>{renderTrigger(selectedItem)}</ListboxButton>}
|
||||
{!renderTrigger && (
|
||||
<Listbox.Button className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover-alt group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<ListboxButton className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover-alt group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||
<span className={classNames('block truncate text-left system-sm-regular text-components-input-text-filled', !selectedItem?.name && 'text-components-input-text-placeholder')}>{selectedItem?.name ?? localPlaceholder}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
{(selectedItem && !notClearable)
|
||||
@ -233,7 +233,7 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</ListboxButton>
|
||||
)}
|
||||
|
||||
{!disabled && (
|
||||
@ -244,9 +244,9 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
||||
<Listbox.Options className={classNames('absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-components-panel-bg-blur py-1 text-base shadow-lg border-components-panel-border border-[0.5px] focus:outline-none sm:text-sm', optionWrapClassName)}>
|
||||
<ListboxOptions className={classNames('absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-components-panel-bg-blur py-1 text-base shadow-lg border-components-panel-border border-[0.5px] focus:outline-none sm:text-sm', optionWrapClassName)}>
|
||||
{items.map((item: Item) => (
|
||||
<Listbox.Option
|
||||
<ListboxOption
|
||||
key={item.value}
|
||||
className={({ active }) =>
|
||||
classNames(
|
||||
@ -275,9 +275,9 @@ const SimpleSelect: FC<ISelectProps> = ({
|
||||
</>)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
</ListboxOption>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,7 +1,8 @@
|
||||
'use client'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { Fragment } from 'react'
|
||||
import { GlobeAltIcon } from '@heroicons/react/24/outline'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type ISelectProps = {
|
||||
items: Array<{ value: string; name: string }>
|
||||
@ -21,14 +22,14 @@ export default function Select({
|
||||
<div className="w-56 text-right">
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<Menu.Button className="inline-flex w-full h-[44px]justify-center items-center
|
||||
<MenuButton className="inline-flex w-full h-[44px]justify-center items-center
|
||||
rounded-lg px-[10px] py-[6px]
|
||||
text-gray-900 text-[13px] font-medium
|
||||
border border-gray-200
|
||||
hover:bg-gray-100">
|
||||
<GlobeAltIcon className="w-5 h-5 mr-1" aria-hidden="true" />
|
||||
{item?.name}
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@ -39,14 +40,13 @@ export default function Select({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 mt-2 w-[200px] origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<MenuItems className="absolute right-0 mt-2 w-[200px] origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<div className="px-1 py-1 ">
|
||||
{items.map((item) => {
|
||||
return <Menu.Item key={item.value}>
|
||||
return <MenuItem key={item.value}>
|
||||
{({ active }) => (
|
||||
<button
|
||||
className={`${active ? 'bg-gray-100' : ''
|
||||
} group flex w-full items-center rounded-lg px-3 py-2 text-sm text-gray-700`}
|
||||
className={cn(active && 'bg-gray-100', 'group flex w-full items-center rounded-lg px-3 py-2 text-sm text-gray-700')}
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault()
|
||||
onChange && onChange(item.value)
|
||||
@ -55,12 +55,12 @@ export default function Select({
|
||||
{item.name}
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
})}
|
||||
|
||||
</div>
|
||||
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
@ -77,9 +77,9 @@ export function InputSelect({
|
||||
<div className="w-full">
|
||||
<Menu as="div" className="w-full">
|
||||
<div>
|
||||
<Menu.Button className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 sm:text-sm h-[38px] text-left">
|
||||
<MenuButton className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 sm:text-sm h-[38px] text-left">
|
||||
{item?.name}
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@ -90,14 +90,13 @@ export function InputSelect({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 mt-2 w-full origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<MenuItems className="absolute right-0 mt-2 w-full origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<div className="px-1 py-1 ">
|
||||
{items.map((item) => {
|
||||
return <Menu.Item key={item.value}>
|
||||
return <MenuItem key={item.value}>
|
||||
{({ active }) => (
|
||||
<button
|
||||
className={`${active ? 'bg-gray-100' : ''
|
||||
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
|
||||
className={`${active ? 'bg-gray-100' : ''} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
|
||||
onClick={() => {
|
||||
onChange && onChange(item.value)
|
||||
}}
|
||||
@ -105,12 +104,12 @@ export function InputSelect({
|
||||
{item.name}
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
})}
|
||||
|
||||
</div>
|
||||
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
|
@ -98,6 +98,7 @@ export enum IndexingType {
|
||||
const DEFAULT_SEGMENT_IDENTIFIER = '\\n\\n'
|
||||
const DEFAULT_MAXIMUM_CHUNK_LENGTH = 500
|
||||
const DEFAULT_OVERLAP = 50
|
||||
const MAXIMUM_CHUNK_TOKEN_LENGTH = parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
|
||||
|
||||
type ParentChildConfig = {
|
||||
chunkForContext: ParentMode
|
||||
@ -163,7 +164,7 @@ const StepTwo = ({
|
||||
doSetSegmentIdentifier(value ? escape(value) : (canEmpty ? '' : DEFAULT_SEGMENT_IDENTIFIER))
|
||||
}, [])
|
||||
const [maxChunkLength, setMaxChunkLength] = useState(DEFAULT_MAXIMUM_CHUNK_LENGTH) // default chunk length
|
||||
const [limitMaxChunkLength, setLimitMaxChunkLength] = useState(4000)
|
||||
const [limitMaxChunkLength, setLimitMaxChunkLength] = useState(MAXIMUM_CHUNK_TOKEN_LENGTH)
|
||||
const [overlap, setOverlap] = useState(DEFAULT_OVERLAP)
|
||||
const [rules, setRules] = useState<PreProcessingRule[]>([])
|
||||
const [defaultConfig, setDefaultConfig] = useState<Rules>()
|
||||
@ -342,8 +343,8 @@ const StepTwo = ({
|
||||
}
|
||||
|
||||
const updatePreview = () => {
|
||||
if (segmentationType === ProcessMode.general && maxChunkLength > 4000) {
|
||||
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
|
||||
if (segmentationType === ProcessMode.general && maxChunkLength > MAXIMUM_CHUNK_TOKEN_LENGTH) {
|
||||
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck', { limit: MAXIMUM_CHUNK_TOKEN_LENGTH }) })
|
||||
return
|
||||
}
|
||||
fetchEstimate()
|
||||
@ -393,7 +394,7 @@ const StepTwo = ({
|
||||
score_threshold_enabled: false,
|
||||
score_threshold: 0.5,
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [rerankDefaultModel, isRerankDefaultModelValid])
|
||||
|
||||
const getCreationParams = () => {
|
||||
|
@ -39,6 +39,8 @@ export const DelimiterInput: FC<InputProps & { tooltip?: string }> = (props) =>
|
||||
}
|
||||
|
||||
export const MaxLengthInput: FC<InputNumberProps> = (props) => {
|
||||
const maxValue = parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
|
||||
|
||||
const { t } = useTranslation()
|
||||
return <FormField label={<div className='system-sm-semibold mb-1'>
|
||||
{t('datasetCreation.stepTwo.maxLength')}
|
||||
@ -46,8 +48,8 @@ export const MaxLengthInput: FC<InputNumberProps> = (props) => {
|
||||
<InputNumber
|
||||
type="number"
|
||||
className='h-9'
|
||||
placeholder={'≤ 4000'}
|
||||
max={4000}
|
||||
placeholder={`≤ ${maxValue}`}
|
||||
max={maxValue}
|
||||
min={1}
|
||||
{...props}
|
||||
/>
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { Tab } from '@headlessui/react'
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
|
||||
import { Tag } from './tag'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
@ -161,7 +161,7 @@ function CodeGroupHeader({ title, children, selectedIndex }: IChildrenProps) {
|
||||
</h3>
|
||||
)}
|
||||
{hasTabs && (
|
||||
<Tab.List className="flex gap-4 -mb-px text-xs font-medium">
|
||||
<TabList className="flex gap-4 -mb-px text-xs font-medium">
|
||||
{Children.map(children, (child, childIndex) => (
|
||||
<Tab
|
||||
className={classNames(
|
||||
@ -174,7 +174,7 @@ function CodeGroupHeader({ title, children, selectedIndex }: IChildrenProps) {
|
||||
{getPanelTitle(child.props.children.props)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
</TabList>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@ -189,13 +189,13 @@ function CodeGroupPanels({ children, targetCode, ...props }: ICodeGroupPanelsPro
|
||||
|
||||
if (hasTabs) {
|
||||
return (
|
||||
<Tab.Panels>
|
||||
<TabPanels>
|
||||
{Children.map(children, child => (
|
||||
<Tab.Panel>
|
||||
<TabPanel>
|
||||
<CodePanel {...props}>{child}</CodePanel>
|
||||
</Tab.Panel>
|
||||
</TabPanel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</TabPanels>
|
||||
)
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@ export function CodeGroup({ children, title, inputs, targetCode, ...props }: ICh
|
||||
)
|
||||
const tabGroupProps = useTabGroupProps(languages)
|
||||
const hasTabs = Children.count(children) > 1
|
||||
const Container = hasTabs ? Tab.Group : 'div'
|
||||
const Container = hasTabs ? TabGroup : 'div'
|
||||
const containerProps = hasTabs ? tabGroupProps : {}
|
||||
const headerProps = hasTabs
|
||||
? { selectedIndex: tabGroupProps.selectedIndex }
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from 'next/navigation'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiArrowDownSLine, RiLogoutBoxRLine } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import Indicator from '../indicator'
|
||||
import AccountAbout from '../account-about'
|
||||
import { mailToSupport } from '../utils/util'
|
||||
@ -59,7 +59,7 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button
|
||||
<MenuButton
|
||||
className={`
|
||||
inline-flex items-center
|
||||
rounded-[20px] py-1 pr-2.5 pl-1 text-sm
|
||||
@ -73,7 +73,7 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
{userProfile.name}
|
||||
<RiArrowDownSLine className="w-3 h-3 ml-1 text-gray-700" />
|
||||
</>}
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -83,14 +83,14 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className="
|
||||
absolute right-0 mt-1.5 w-60 max-w-80
|
||||
divide-y divide-divider-subtle origin-top-right rounded-lg bg-components-panel-bg-blur
|
||||
shadow-lg focus:outline-none
|
||||
"
|
||||
>
|
||||
<Menu.Item disabled>
|
||||
<MenuItem disabled>
|
||||
<div className='flex flex-nowrap items-center px-4 py-[13px]'>
|
||||
<Avatar name={userProfile.name} size={36} className='mr-3' />
|
||||
<div className='grow'>
|
||||
@ -98,13 +98,13 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div className='system-xs-regular text-text-tertiary break-all'>{userProfile.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
<div className='px-1 py-1'>
|
||||
<div className='mt-2 px-3 text-xs font-medium text-text-tertiary'>{t('common.userProfile.workspace')}</div>
|
||||
<WorkplaceSelector />
|
||||
</div>
|
||||
<div className="px-1 py-1">
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
{({ active }) => <Link
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -114,15 +114,15 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.account.account')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</Link>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{({ active }) => <div className={classNames(itemClassName,
|
||||
active && 'bg-state-base-hover',
|
||||
)} onClick={() => setShowAccountSettingModal({ payload: 'members' })}>
|
||||
<div>{t('common.userProfile.settings')}</div>
|
||||
</div>}
|
||||
</Menu.Item>
|
||||
{canEmailSupport && <Menu.Item>
|
||||
</MenuItem>
|
||||
{canEmailSupport && <MenuItem>
|
||||
{({ active }) => <a
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -132,8 +132,8 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.userProfile.emailSupport')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</a>}
|
||||
</Menu.Item>}
|
||||
<Menu.Item>
|
||||
</MenuItem>}
|
||||
<MenuItem>
|
||||
{({ active }) => <Link
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -143,8 +143,8 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.userProfile.communityFeedback')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</Link>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{({ active }) => <Link
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -154,8 +154,8 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.userProfile.community')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</Link>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{({ active }) => <Link
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -167,8 +167,8 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.userProfile.helpCenter')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</Link>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{({ active }) => <Link
|
||||
className={classNames(itemClassName, 'group justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
@ -178,10 +178,10 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<div>{t('common.userProfile.roadmap')}</div>
|
||||
<ArrowUpRight className='hidden w-[14px] h-[14px] text-text-tertiary group-hover:flex' />
|
||||
</Link>}
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
{
|
||||
document?.body?.getAttribute('data-public-site-about') !== 'hide' && (
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
{({ active }) => <div className={classNames(itemClassName, 'justify-between',
|
||||
active && 'bg-state-base-hover',
|
||||
)} onClick={() => setAboutVisible(true)}>
|
||||
@ -191,11 +191,11 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<Indicator color={langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version ? 'green' : 'orange'} />
|
||||
</div>
|
||||
</div>}
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
{({ active }) => <div className='p-1' onClick={() => handleLogout()}>
|
||||
<div
|
||||
className={
|
||||
@ -206,8 +206,8 @@ export default function AppSelector({ isMobile }: IAppSelector) {
|
||||
<RiLogoutBoxRLine className='hidden w-4 h-4 text-text-tertiary group-hover:flex' />
|
||||
</div>
|
||||
</div>}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Fragment } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import { switchWorkspace } from '@/service/common'
|
||||
@ -48,7 +48,7 @@ const WorkplaceSelector = () => {
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={cn(
|
||||
<MenuButton className={cn(
|
||||
`
|
||||
${itemClassName} w-full
|
||||
group hover:bg-state-base-hover cursor-pointer ${open && 'bg-state-base-hover'} rounded-lg
|
||||
@ -57,7 +57,7 @@ const WorkplaceSelector = () => {
|
||||
<div className={itemIconClassName}>{currentWorkspace?.name[0].toLocaleUpperCase()}</div>
|
||||
<div className={`${itemNameClassName} truncate`}>{currentWorkspace?.name}</div>
|
||||
<ChevronRight className='shrink-0 w-[14px] h-[14px] text-gray-500' />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -67,7 +67,7 @@ const WorkplaceSelector = () => {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className={cn(
|
||||
`
|
||||
absolute top-[1px] min-w-[200px] max-h-[70vh] overflow-y-scroll z-10 bg-white border-[0.5px] border-gray-200
|
||||
@ -79,7 +79,7 @@ const WorkplaceSelector = () => {
|
||||
<div className="px-1 py-1">
|
||||
{
|
||||
workspaces.map(workspace => (
|
||||
<Menu.Item key={workspace.id}>
|
||||
<MenuItem key={workspace.id}>
|
||||
{({ active }) => <div className={classNames(itemClassName,
|
||||
active && 'bg-state-base-hover',
|
||||
)} key={workspace.id} onClick={() => handleSwitchWorkspace(workspace.id)}>
|
||||
@ -88,11 +88,11 @@ const WorkplaceSelector = () => {
|
||||
{workspace.current && <Check className={itemCheckClassName} />}
|
||||
</div>}
|
||||
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
RiMoreFill,
|
||||
RiStickyNoteAddLine,
|
||||
} from '@remixicon/react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { syncDataSourceNotion, updateDataSourceNotionAction } from '@/service/common'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
|
||||
@ -54,9 +54,9 @@ export default function Operate({
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={`flex items-center justify-center w-8 h-8 rounded-lg hover:bg-gray-100 ${open && 'bg-gray-100'}`}>
|
||||
<MenuButton className={`flex items-center justify-center w-8 h-8 rounded-lg hover:bg-gray-100 ${open && 'bg-gray-100'}`}>
|
||||
<RiMoreFill className='w-4 h-4' />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -66,7 +66,7 @@ export default function Operate({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className="
|
||||
absolute right-0 top-9 w-60 max-w-80
|
||||
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
|
||||
@ -74,7 +74,7 @@ export default function Operate({
|
||||
"
|
||||
>
|
||||
<div className="px-1 py-1">
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
<div
|
||||
className={itemClassName}
|
||||
onClick={onAuthAgain}
|
||||
@ -87,23 +87,23 @@ export default function Operate({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<div className={itemClassName} onClick={handleSync}>
|
||||
<RiLoopLeftLine className={itemIconClassName} />
|
||||
<div className='leading-5'>{t('common.dataSource.notion.sync')}</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
</div>
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
<div className='p-1'>
|
||||
<div className={itemClassName} onClick={handleRemove}>
|
||||
<RiDeleteBinLine className={itemIconClassName} />
|
||||
<div className='leading-5'>{t('common.dataSource.notion.remove')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/24/outline'
|
||||
import s from './index.module.css'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
@ -90,7 +90,7 @@ const Operation = ({
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={cn(
|
||||
<MenuButton className={cn(
|
||||
`
|
||||
group flex items-center justify-between w-full h-full
|
||||
hover:bg-gray-100 cursor-pointer ${open && 'bg-gray-100'}
|
||||
@ -99,7 +99,7 @@ const Operation = ({
|
||||
)}>
|
||||
{RoleMap[member.role] || RoleMap.normal}
|
||||
<ChevronDownIcon className={`w-4 h-4 group-hover:block ${open ? 'block' : 'hidden'}`} />
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -109,7 +109,7 @@ const Operation = ({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className={cn(
|
||||
`
|
||||
absolute right-0 top-[52px] z-10 bg-white border-[0.5px] border-gray-200
|
||||
@ -121,7 +121,7 @@ const Operation = ({
|
||||
<div className="px-1 py-1">
|
||||
{
|
||||
roleList.map(role => (
|
||||
<Menu.Item key={role}>
|
||||
<MenuItem key={role}>
|
||||
<div className={itemClassName} onClick={() => handleUpdateMemberRole(role)}>
|
||||
{
|
||||
role === member.role
|
||||
@ -133,11 +133,11 @@ const Operation = ({
|
||||
<div className={itemDescClassName}>{t(`common.members.${toHump(role)}Tip`)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<Menu.Item>
|
||||
<MenuItem>
|
||||
<div className='px-1 py-1'>
|
||||
<div className={itemClassName} onClick={handleDeleteMemberOrCancelInvitation}>
|
||||
<div className={itemIconClassName} />
|
||||
@ -147,8 +147,8 @@ const Operation = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Fragment } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiCheckLine,
|
||||
@ -31,7 +31,7 @@ const Selector: FC<SelectorProps> = ({
|
||||
|
||||
return (
|
||||
<Popover className='relative'>
|
||||
<Popover.Button>
|
||||
<PopoverButton>
|
||||
{
|
||||
({ open }) => (
|
||||
<Button className={`
|
||||
@ -42,19 +42,19 @@ const Selector: FC<SelectorProps> = ({
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</Popover.Button>
|
||||
</PopoverButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave='transition ease-in duration-100'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
>
|
||||
<Popover.Panel className='absolute top-7 right-0 w-[144px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg z-10'>
|
||||
<PopoverPanel className='absolute top-7 right-0 w-[144px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg z-10'>
|
||||
<div className='p-1'>
|
||||
<div className='px-3 pt-2 pb-1 text-sm font-medium text-gray-700'>{t('common.modelProvider.card.priorityUse')}</div>
|
||||
{
|
||||
options.map(option => (
|
||||
<Popover.Button as={Fragment} key={option.key}>
|
||||
<PopoverButton as={Fragment} key={option.key}>
|
||||
<div
|
||||
className='flex items-center justify-between px-3 h-9 text-sm text-gray-700 rounded-lg cursor-pointer hover:bg-gray-50'
|
||||
onClick={() => onSelect(option.key)}
|
||||
@ -62,11 +62,11 @@ const Selector: FC<SelectorProps> = ({
|
||||
<div className='grow'>{option.text}</div>
|
||||
{value === option.key && <RiCheckLine className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
</Popover.Button>
|
||||
</PopoverButton>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</PopoverPanel>
|
||||
</Transition>
|
||||
</Popover>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { ChevronDownIcon, PlusIcon } from '@heroicons/react/24/solid'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import Indicator from '../indicator'
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
@ -30,7 +30,7 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
<div className="">
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<Menu.Button
|
||||
<MenuButton
|
||||
className="
|
||||
inline-flex items-center w-full h-7 justify-center
|
||||
rounded-[10px] pl-2 pr-2.5 text-[14px] font-semibold
|
||||
@ -42,7 +42,7 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
className="w-3 h-3 ml-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@ -53,7 +53,7 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
<MenuItems
|
||||
className="
|
||||
absolute -left-11 right-0 mt-1.5 w-60 max-w-80
|
||||
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
|
||||
@ -63,7 +63,7 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
{!!appItems.length && (<div className="px-1 py-1 overflow-auto" style={{ maxHeight: '50vh' }}>
|
||||
{
|
||||
appItems.map((app: AppDetailResponse) => (
|
||||
<Menu.Item key={app.id}>
|
||||
<MenuItem key={app.id}>
|
||||
<div className={itemClassName} onClick={() =>
|
||||
router.push(`/app/${app.id}/${isCurrentWorkspaceEditor ? 'configuration' : 'overview'}`)
|
||||
}>
|
||||
@ -75,11 +75,11 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
</div>
|
||||
{app.name}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</div>)}
|
||||
{isCurrentWorkspaceEditor && <Menu.Item>
|
||||
{isCurrentWorkspaceEditor && <MenuItem>
|
||||
<div className='p-1' onClick={() => setShowNewAppDialog(true)}>
|
||||
<div
|
||||
className='flex items-center h-12 rounded-lg cursor-pointer hover:bg-gray-100'
|
||||
@ -96,15 +96,15 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
|
||||
<div className='font-normal text-[14px] text-gray-700'>{t('common.menus.newApp')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
}
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<CreateAppDialog
|
||||
show={showNewAppDialog}
|
||||
onClose={() => setShowNewAppDialog(false)}
|
||||
onSuccess={() => {}}
|
||||
onSuccess={() => { }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
RiArrowDownSLine,
|
||||
RiArrowRightSLine,
|
||||
} from '@remixicon/react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { debounce } from 'lodash-es'
|
||||
import cn from '@/utils/classnames'
|
||||
@ -57,7 +57,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={cn(
|
||||
<MenuButton className={cn(
|
||||
'group inline-flex items-center w-full h-7 justify-center rounded-[10px] pl-2 pr-2.5 text-[14px] font-semibold text-components-main-nav-nav-button-text-active hover:hover:bg-components-main-nav-nav-button-bg-active-hover',
|
||||
open && 'bg-components-main-nav-nav-button-bg-active',
|
||||
)}>
|
||||
@ -66,8 +66,8 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
className={cn('shrink-0 w-3 h-3 ml-1 opacity-50 group-hover:opacity-100', open && '!opacity-100')}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
<Menu.Items
|
||||
</MenuButton>
|
||||
<MenuItems
|
||||
className="
|
||||
absolute -left-11 right-0 mt-1.5 w-60 max-w-80
|
||||
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
|
||||
@ -77,7 +77,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
<div className="px-1 py-1 overflow-auto" style={{ maxHeight: '50vh' }} onScroll={handleScroll}>
|
||||
{
|
||||
navs.map(nav => (
|
||||
<Menu.Item key={nav.id}>
|
||||
<MenuItem key={nav.id}>
|
||||
<div className='flex items-center w-full px-3 py-[6px] text-gray-700 text-[14px] rounded-lg font-normal hover:bg-gray-100 cursor-pointer truncate' onClick={() => {
|
||||
if (curNav?.id === nav.id)
|
||||
return
|
||||
@ -85,7 +85,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
router.push(nav.link)
|
||||
}} title={nav.name}>
|
||||
<div className='relative w-6 h-6 mr-2 rounded-md'>
|
||||
<AppIcon size='tiny' iconType={nav.icon_type} icon={nav.icon} background={nav.icon_background} imageUrl={nav.icon_url}/>
|
||||
<AppIcon size='tiny' iconType={nav.icon_type} icon={nav.icon} background={nav.icon_background} imageUrl={nav.icon_url} />
|
||||
{!!nav.mode && (
|
||||
<span className={cn(
|
||||
'absolute w-3.5 h-3.5 -bottom-0.5 -right-0.5 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm',
|
||||
@ -112,12 +112,12 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
{nav.name}
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{!isApp && isCurrentWorkspaceEditor && (
|
||||
<Menu.Button className='p-1 w-full'>
|
||||
<MenuButton className='p-1 w-full'>
|
||||
<div onClick={() => onCreate('')} className={cn(
|
||||
'flex items-center gap-2 px-3 py-[6px] rounded-lg cursor-pointer hover:bg-gray-100',
|
||||
)}>
|
||||
@ -126,13 +126,13 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
</div>
|
||||
<div className='grow text-left font-normal text-[14px] text-gray-700'>{createText}</div>
|
||||
</div>
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
)}
|
||||
{isApp && isCurrentWorkspaceEditor && (
|
||||
<Menu as="div" className="relative w-full h-full">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Menu.Button className='p-1 w-full'>
|
||||
<MenuButton className='p-1 w-full'>
|
||||
<div className={cn(
|
||||
'flex items-center gap-2 px-3 py-[6px] rounded-lg cursor-pointer hover:bg-gray-100',
|
||||
open && '!bg-gray-100',
|
||||
@ -143,7 +143,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
<div className='grow text-left font-normal text-[14px] text-gray-700'>{createText}</div>
|
||||
<RiArrowRightSLine className='shrink-0 w-3.5 h-3.5 text-gray-500' />
|
||||
</div>
|
||||
</Menu.Button>
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -153,7 +153,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className={cn(
|
||||
<MenuItems className={cn(
|
||||
'absolute top-[3px] right-[-198px] min-w-[200px] z-10 bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg',
|
||||
)}>
|
||||
<div className='p-1'>
|
||||
@ -172,13 +172,13 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
|
||||
{t('app.importDSL')}
|
||||
</div>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
)}
|
||||
</Menu.Items>
|
||||
</MenuItems>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
|
@ -7,10 +7,10 @@ export type II18NServerProps = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const I18NServer = ({
|
||||
const I18NServer = async ({
|
||||
children,
|
||||
}: II18NServerProps) => {
|
||||
const locale = getLocaleOnServer()
|
||||
const locale = await getLocaleOnServer()
|
||||
|
||||
return (
|
||||
<I18N {...{ locale }}>
|
||||
|
@ -19,12 +19,12 @@ export const viewport: Viewport = {
|
||||
userScalable: false,
|
||||
}
|
||||
|
||||
const LocaleLayout = ({
|
||||
const LocaleLayout = async ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const locale = getLocaleOnServer()
|
||||
const locale = await getLocaleOnServer()
|
||||
|
||||
return (
|
||||
<html lang={locale ?? 'en'} className="h-full" data-theme="light">
|
||||
@ -45,6 +45,7 @@ const LocaleLayout = ({
|
||||
data-public-site-about={process.env.NEXT_PUBLIC_SITE_ABOUT}
|
||||
data-public-text-generation-timeout-ms={process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS}
|
||||
data-public-top-k-max-value={process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE}
|
||||
data-public-indexing-max-segmentation-tokens-length={process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
|
||||
>
|
||||
<BrowserInitor>
|
||||
<SentryInitor>
|
||||
|
@ -24,5 +24,6 @@ export NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED}
|
||||
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
|
||||
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
|
||||
export NEXT_PUBLIC_TOP_K_MAX_VALUE=${TOP_K_MAX_VALUE}
|
||||
export NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
|
||||
|
||||
pm2 start ./pm2.json --no-daemon
|
||||
|
@ -30,18 +30,19 @@ export async function useTranslation(lng: Locale, ns = '', options: Record<strin
|
||||
}
|
||||
}
|
||||
|
||||
export const getLocaleOnServer = (): Locale => {
|
||||
export const getLocaleOnServer = async (): Promise<Locale> => {
|
||||
const locales: string[] = i18n.locales
|
||||
|
||||
let languages: string[] | undefined
|
||||
// get locale from cookie
|
||||
const localeCookie = cookies().get('locale')
|
||||
const cookieStore = await cookies()
|
||||
const localeCookie = cookieStore.get('locale')
|
||||
languages = localeCookie?.value ? [localeCookie.value] : []
|
||||
|
||||
if (!languages.length) {
|
||||
// Negotiator expects plain object so we need to transform headers
|
||||
const negotiatorHeaders: Record<string, string> = {}
|
||||
headers().forEach((value, key) => (negotiatorHeaders[key] = value))
|
||||
const negotiatorHeaders: Record<string, string> = {};
|
||||
(await headers()).forEach((value, key) => (negotiatorHeaders[key] = value))
|
||||
// Use negotiator and intl-localematcher to get best locale
|
||||
languages = new Negotiator({ headers: negotiatorHeaders }).languages()
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"version": "0.15.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=18.17.0"
|
||||
"node": ">=18.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@ -28,14 +28,14 @@
|
||||
"@emoji-mart/data": "^1.1.2",
|
||||
"@floating-ui/react": "^0.25.2",
|
||||
"@formatjs/intl-localematcher": "^0.5.4",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@headlessui/react": "^2.2.0",
|
||||
"@heroicons/react": "^2.0.16",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@lexical/react": "^0.16.0",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@next/mdx": "^14.0.4",
|
||||
"@next/mdx": "15.1.5",
|
||||
"@remixicon/react": "^4.5.0",
|
||||
"@sentry/react": "^7.54.0",
|
||||
"@sentry/utils": "^7.54.0",
|
||||
@ -69,14 +69,14 @@
|
||||
"mermaid": "11.4.1",
|
||||
"mime": "^4.0.4",
|
||||
"negotiator": "^0.6.3",
|
||||
"next": "^14.2.10",
|
||||
"next": "15.1.5",
|
||||
"pinyin-pro": "^3.23.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qs": "^6.11.1",
|
||||
"rc-textarea": "^1.5.2",
|
||||
"react": "~18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-18-input-autosize": "^3.0.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-easy-crop": "^5.0.8",
|
||||
"react-error-boundary": "^4.0.2",
|
||||
"react-hook-form": "^7.51.4",
|
||||
@ -118,15 +118,15 @@
|
||||
"@chromatic-com/storybook": "^1.9.0",
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@rgrove/parse-xml": "^4.1.0",
|
||||
"@storybook/addon-essentials": "^8.3.5",
|
||||
"@storybook/addon-interactions": "^8.3.5",
|
||||
"@storybook/addon-links": "^8.3.5",
|
||||
"@storybook/addon-onboarding": "^8.3.5",
|
||||
"@storybook/addon-themes": "^8.3.5",
|
||||
"@storybook/blocks": "^8.3.5",
|
||||
"@storybook/nextjs": "^8.3.5",
|
||||
"@storybook/react": "^8.3.5",
|
||||
"@storybook/test": "^8.3.5",
|
||||
"@storybook/addon-essentials": "8.5.0",
|
||||
"@storybook/addon-interactions": "8.5.0",
|
||||
"@storybook/addon-links": "8.5.0",
|
||||
"@storybook/addon-onboarding": "8.5.0",
|
||||
"@storybook/addon-themes": "8.5.0",
|
||||
"@storybook/blocks": "8.5.0",
|
||||
"@storybook/nextjs": "8.5.0",
|
||||
"@storybook/react": "8.5.0",
|
||||
"@storybook/test": "8.5.0",
|
||||
"@testing-library/dom": "^10.3.2",
|
||||
"@testing-library/jest-dom": "^6.4.6",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
@ -138,8 +138,8 @@
|
||||
"@types/negotiator": "^0.6.1",
|
||||
"@types/node": "18.15.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/react": "~18.2.0",
|
||||
"@types/react-dom": "~18.2.0",
|
||||
"@types/react": "19.0.7",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@types/react-slider": "^1.3.1",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"@types/react-window": "^1.8.5",
|
||||
@ -152,7 +152,7 @@
|
||||
"code-inspector-plugin": "^0.18.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-next": "^14.0.4",
|
||||
"eslint-config-next": "15.1.5",
|
||||
"eslint-plugin-storybook": "^0.9.0",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.7.0",
|
||||
@ -161,7 +161,7 @@
|
||||
"magicast": "^0.3.4",
|
||||
"postcss": "^8.4.31",
|
||||
"sass": "^1.61.0",
|
||||
"storybook": "^8.3.5",
|
||||
"storybook": "8.5.0",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "4.9.5",
|
||||
@ -179,5 +179,10 @@
|
||||
"**/*.ts?(x)": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "19.0.7",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@storybook/test": "8.5.0"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user