Jelajahi Sumber

Revert "Feat/parent child retrieval" (#12095)

-LAN- 4 bulan lalu
induk
melakukan
db2aa83a7c
100 mengubah file dengan 1513 tambahan dan 3426 penghapusan
  1. 1 12
      api/poetry.lock
  2. 95 61
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
  3. 3 3
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx
  4. 9 0
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css
  5. 5 12
      web/app/(commonLayout)/datasets/Container.tsx
  6. 5 5
      web/app/(commonLayout)/datasets/DatasetCard.tsx
  7. 0 45
      web/app/components/app-sidebar/dataset-info.tsx
  8. 11 19
      web/app/components/app-sidebar/index.tsx
  9. 6 10
      web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
  10. 0 19
      web/app/components/base/app-icon/style.module.css
  11. 0 2
      web/app/components/base/auto-height-textarea/common.tsx
  12. 2 4
      web/app/components/base/badge.tsx
  13. 0 5
      web/app/components/base/checkbox/assets/mixed.svg
  14. 0 10
      web/app/components/base/checkbox/index.module.css
  15. 1 4
      web/app/components/base/checkbox/index.tsx
  16. 1 1
      web/app/components/base/divider/index.tsx
  17. 0 23
      web/app/components/base/divider/with-label.tsx
  18. 1 3
      web/app/components/base/drawer/index.tsx
  19. 5 2
      web/app/components/base/file-uploader/file-type-icon.tsx
  20. 0 13
      web/app/components/base/icons/assets/public/knowledge/chunk.svg
  21. 0 9
      web/app/components/base/icons/assets/public/knowledge/collapse.svg
  22. 0 2
      web/app/components/base/icons/assets/public/knowledge/general-type.svg
  23. 0 5
      web/app/components/base/icons/assets/public/knowledge/layout-right-2-line-mod.svg
  24. 0 7
      web/app/components/base/icons/assets/public/knowledge/parent-child-type.svg
  25. 0 13
      web/app/components/base/icons/assets/public/knowledge/selection-mod.svg
  26. 0 116
      web/app/components/base/icons/src/public/knowledge/Chunk.json
  27. 0 16
      web/app/components/base/icons/src/public/knowledge/Chunk.tsx
  28. 0 62
      web/app/components/base/icons/src/public/knowledge/Collapse.json
  29. 0 16
      web/app/components/base/icons/src/public/knowledge/Collapse.tsx
  30. 0 27
      web/app/components/base/icons/src/public/knowledge/GeneralType.json
  31. 0 16
      web/app/components/base/icons/src/public/knowledge/GeneralType.tsx
  32. 0 36
      web/app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.json
  33. 0 16
      web/app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.tsx
  34. 0 56
      web/app/components/base/icons/src/public/knowledge/ParentChildType.json
  35. 0 16
      web/app/components/base/icons/src/public/knowledge/ParentChildType.tsx
  36. 0 116
      web/app/components/base/icons/src/public/knowledge/SelectionMod.json
  37. 0 16
      web/app/components/base/icons/src/public/knowledge/SelectionMod.tsx
  38. 0 6
      web/app/components/base/icons/src/public/knowledge/index.ts
  39. 1 1
      web/app/components/base/icons/src/vender/features/index.ts
  40. 0 86
      web/app/components/base/input-number/index.tsx
  41. 0 62
      web/app/components/base/linked-apps-panel/index.tsx
  42. 1 1
      web/app/components/base/pagination/index.tsx
  43. 15 21
      web/app/components/base/param-item/index.tsx
  44. 9 14
      web/app/components/base/radio-card/index.tsx
  45. 85 0
      web/app/components/base/retry-button/index.tsx
  46. 4 0
      web/app/components/base/retry-button/style.module.css
  47. 3 4
      web/app/components/base/simple-pie-chart/index.tsx
  48. 5 8
      web/app/components/base/skeleton/index.tsx
  49. 0 3
      web/app/components/base/switch/index.tsx
  50. 24 27
      web/app/components/base/tag-input/index.tsx
  51. 7 12
      web/app/components/base/toast/index.tsx
  52. 1 3
      web/app/components/base/tooltip/index.tsx
  53. 7 12
      web/app/components/billing/priority-label/index.tsx
  54. 0 54
      web/app/components/datasets/chunk.tsx
  55. 0 29
      web/app/components/datasets/common/chunking-mode-label.tsx
  56. 0 40
      web/app/components/datasets/common/document-file-icon.tsx
  57. 0 42
      web/app/components/datasets/common/document-picker/document-list.tsx
  58. 0 118
      web/app/components/datasets/common/document-picker/index.tsx
  59. 0 82
      web/app/components/datasets/common/document-picker/preview-document-picker.tsx
  60. 0 38
      web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx
  61. 0 69
      web/app/components/datasets/common/document-status-with-action/index-failed.tsx
  62. 0 65
      web/app/components/datasets/common/document-status-with-action/status-with-action.tsx
  63. 14 13
      web/app/components/datasets/common/economical-retrieval-method-config/index.tsx
  64. 41 48
      web/app/components/datasets/common/retrieval-method-config/index.tsx
  65. 10 10
      web/app/components/datasets/common/retrieval-method-info/index.tsx
  66. 18 20
      web/app/components/datasets/common/retrieval-param-config/index.tsx
  67. 0 6
      web/app/components/datasets/create/assets/family-mod.svg
  68. 0 5
      web/app/components/datasets/create/assets/file-list-3-fill.svg
  69. 0 1
      web/app/components/datasets/create/assets/gold.svg
  70. 0 5
      web/app/components/datasets/create/assets/note-mod.svg
  71. 0 12
      web/app/components/datasets/create/assets/option-card-effect-blue.svg
  72. 0 12
      web/app/components/datasets/create/assets/option-card-effect-orange.svg
  73. 0 12
      web/app/components/datasets/create/assets/option-card-effect-purple.svg
  74. 0 12
      web/app/components/datasets/create/assets/pattern-recognition-mod.svg
  75. 0 7
      web/app/components/datasets/create/assets/piggy-bank-mod.svg
  76. 0 8
      web/app/components/datasets/create/assets/progress-indicator.svg
  77. 0 13
      web/app/components/datasets/create/assets/rerank.svg
  78. 0 6
      web/app/components/datasets/create/assets/research-mod.svg
  79. 0 12
      web/app/components/datasets/create/assets/selection-mod.svg
  80. 0 1
      web/app/components/datasets/create/assets/setting-gear-mod.svg
  81. 39 13
      web/app/components/datasets/create/embedding-process/index.module.css
  82. 55 137
      web/app/components/datasets/create/embedding-process/index.tsx
  83. 1 2
      web/app/components/datasets/create/file-preview/index.module.css
  84. 2 2
      web/app/components/datasets/create/file-preview/index.tsx
  85. 66 1
      web/app/components/datasets/create/file-uploader/index.module.css
  86. 28 54
      web/app/components/datasets/create/file-uploader/index.tsx
  87. 0 16
      web/app/components/datasets/create/icons.ts
  88. 28 30
      web/app/components/datasets/create/index.tsx
  89. 2 2
      web/app/components/datasets/create/notion-page-preview/index.tsx
  90. 23 15
      web/app/components/datasets/create/step-one/index.module.css
  91. 127 160
      web/app/components/datasets/create/step-one/index.tsx
  92. 21 32
      web/app/components/datasets/create/step-three/index.tsx
  93. 30 8
      web/app/components/datasets/create/step-two/index.module.css
  94. 692 843
      web/app/components/datasets/create/step-two/index.tsx
  95. 0 77
      web/app/components/datasets/create/step-two/inputs.tsx
  96. 9 24
      web/app/components/datasets/create/step-two/language-select/index.tsx
  97. 0 98
      web/app/components/datasets/create/step-two/option-card.tsx
  98. 0 27
      web/app/components/datasets/create/stepper/index.tsx
  99. 0 46
      web/app/components/datasets/create/stepper/step.tsx
  100. 0 41
      web/app/components/datasets/create/top-bar/index.tsx

+ 1 - 12
api/poetry.lock

@@ -1,15 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
-
-[[package]]
-name = "aiofiles"
-version = "24.1.0"
-description = "File support for asyncio."
-optional = false
-python-versions = ">=3.8"
-files = [
-    {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
-    {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
-]
+# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
 
 [[package]]
 name = "aiofiles"

+ 95 - 61
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx

@@ -7,36 +7,85 @@ import { useTranslation } from 'react-i18next'
 import { useBoolean } from 'ahooks'
 import {
   Cog8ToothIcon,
+  // CommandLineIcon,
+  Squares2X2Icon,
+  // eslint-disable-next-line sort-imports
+  PuzzlePieceIcon,
   DocumentTextIcon,
   PaperClipIcon,
+  QuestionMarkCircleIcon,
 } 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 Link from 'next/link'
 import s from './style.module.css'
 import classNames from '@/utils/classnames'
 import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
-import type { RelatedAppResponse } from '@/models/datasets'
+import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
 import AppSideBar from '@/app/components/app-sidebar'
+import Divider from '@/app/components/base/divider'
+import AppIcon from '@/app/components/base/app-icon'
 import Loading from '@/app/components/base/loading'
+import FloatPopoverContainer from '@/app/components/base/float-popover-container'
 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 { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
+import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
 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
   params: { datasetId: string }
 }
 
+type ILikedItemProps = {
+  type?: 'plugin' | 'app'
+  appStatus?: boolean
+  detail: RelatedApp
+  isMobile: boolean
+}
+
+const LikedItem = ({
+  type = 'app',
+  detail,
+  isMobile,
+}: ILikedItemProps) => {
+  return (
+    <Link className={classNames(s.itemWrapper, 'px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
+      <div className={classNames(s.iconWrapper, 'mr-0')}>
+        <AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
+        {type === 'app' && (
+          <span className='absolute bottom-[-2px] right-[-2px] w-3.5 h-3.5 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
+            {detail.mode === 'advanced-chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'agent-chat' && (
+              <CuteRobot className='w-2.5 h-2.5 text-indigo-600' />
+            )}
+            {detail.mode === 'chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'completion' && (
+              <AiText className='w-2.5 h-2.5 text-[#0E9384]' />
+            )}
+            {detail.mode === 'workflow' && (
+              <Route className='w-2.5 h-2.5 text-[#f79009]' />
+            )}
+          </span>
+        )}
+      </div>
+      {!isMobile && <div className={classNames(s.appInfo, 'ml-2')}>{detail?.name || '--'}</div>}
+    </Link>
+  )
+}
+
 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)">
@@ -68,80 +117,65 @@ const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
 type IExtraInfoProps = {
   isMobile: boolean
   relatedApps?: RelatedAppResponse
-  expand: boolean
 }
 
-const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
+const ExtraInfo = ({ isMobile, relatedApps }: 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 && (
+  return <div className='w-full flex flex-col items-center'>
+    <Divider className='mt-5' />
+    {(relatedApps?.data && relatedApps?.data?.length > 0) && (
       <>
-        {!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='w-full px-2 pb-1 pt-4 uppercase text-xs text-gray-500 font-medium'>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
         {isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
-          {relatedAppsTotal || '--'}
+          {relatedApps?.total || '--'}
           <PaperClipIcon className='h-4 w-4 text-gray-700' />
         </div>}
+        {relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
       </>
     )}
-    {!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>
+    {!relatedApps?.data?.length && (
+      <FloatPopoverContainer
+        placement='bottom-start'
+        open={isShowTips}
+        toggle={toggleTips}
+        isMobile={isMobile}
+        triggerElement={
+          <div className={classNames('h-7 w-7 inline-flex justify-center items-center rounded-lg bg-transparent', isShowTips && '!bg-gray-50')}>
+            <QuestionMarkCircleIcon className='h-4 w-4 flex-shrink-0 text-gray-500' />
           </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 className={classNames('mt-5 p-3', isMobile && 'border-[0.5px] border-gray-200 shadow-lg rounded-lg bg-white w-[160px]')}>
+          <div className='flex items-center justify-start gap-2'>
+            <div className={s.emptyIconDiv}>
+              <Squares2X2Icon className='w-3 h-3 text-gray-500' />
+            </div>
+            <div className={s.emptyIconDiv}>
+              <PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
+            </div>
+          </div>
+          <div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
+          <a
+            className='inline-flex items-center text-xs text-primary-600 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>
-      </Tooltip>
+      </FloatPopoverContainer>
     )}
   </div>
 }
@@ -201,7 +235,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   }, [isMobile, setAppSiderbarExpand])
 
   if (!datasetRes && !error)
-    return <Loading type='app' />
+    return <Loading />
 
   return (
     <div className='grow flex overflow-hidden'>
@@ -212,7 +246,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
         desc={datasetRes?.description || '--'}
         isExternal={datasetRes?.provider === 'external'}
         navigation={navigation}
-        extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : undefined}
+        extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} /> : undefined}
         iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
       />}
       <DatasetDetailContext.Provider value={{
@@ -220,7 +254,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
         dataset: datasetRes,
         mutateDatasetRes: () => mutateDatasetRes(),
       }}>
-        <div className="bg-background-default-subtle grow overflow-hidden">{children}</div>
+        <div className="bg-white grow overflow-hidden">{children}</div>
       </DatasetDetailContext.Provider>
     </div>
   )

+ 3 - 3
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx

@@ -7,10 +7,10 @@ const Settings = async () => {
   const { t } = await translate(locale, 'dataset-settings')
 
   return (
-    <div className='h-full overflow-y-auto'>
+    <div className='bg-white h-full overflow-y-auto'>
       <div className='px-6 py-3'>
-        <div className='mb-1 system-xl-semibold text-text-primary'>{t('title')}</div>
-        <div className='system-sm-regular text-text-tertiary'>{t('desc')}</div>
+        <div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
+        <div className='text-sm text-gray-500'>{t('desc')}</div>
       </div>
       <Form />
     </div>

+ 9 - 0
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css

@@ -1,3 +1,12 @@
+.itemWrapper {
+  @apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
+}
+.appInfo {
+  @apply truncate text-gray-700 text-sm font-normal;
+}
+.iconWrapper {
+  @apply relative w-6 h-6 rounded-lg;
+}
 .statusPoint {
   @apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
 }

+ 5 - 12
web/app/(commonLayout)/datasets/Container.tsx

@@ -17,6 +17,7 @@ import TagManagementModal from '@/app/components/base/tag-management'
 import TagFilter from '@/app/components/base/tag-management/filter'
 import Button from '@/app/components/base/button'
 import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
+import SearchInput from '@/app/components/base/search-input'
 
 // Services
 import { fetchDatasetApiBaseUrl } from '@/service/datasets'
@@ -28,7 +29,6 @@ import { useAppContext } from '@/context/app-context'
 import { useExternalApiPanel } from '@/context/external-api-panel-context'
 // eslint-disable-next-line import/order
 import { useQuery } from '@tanstack/react-query'
-import Input from '@/app/components/base/input'
 
 const Container = () => {
   const { t } = useTranslation()
@@ -81,24 +81,17 @@ const Container = () => {
   }, [currentWorkspace, router])
 
   return (
-    <div ref={containerRef} className='grow relative flex flex-col bg-background-body overflow-y-auto'>
-      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
+    <div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
+      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
         <TabSliderNew
           value={activeTab}
           onChange={newActiveTab => setActiveTab(newActiveTab)}
           options={options}
         />
         {activeTab === 'dataset' && (
-          <div className='flex items-center justify-center gap-2'>
+          <div className='flex items-center gap-2'>
             <TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
-            <Input
-              showLeftIcon
-              showClearIcon
-              wrapperClassName='w-[200px]'
-              value={keywords}
-              onChange={e => handleKeywordsChange(e.target.value)}
-              onClear={() => handleKeywordsChange('')}
-            />
+            <SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
             <div className="w-[1px] h-4 bg-divider-regular" />
             <Button
               className='gap-0.5 shadows-shadow-xs'

+ 5 - 5
web/app/(commonLayout)/datasets/DatasetCard.tsx

@@ -111,7 +111,7 @@ const DatasetCard = ({
   return (
     <>
       <div
-        className='group relative col-span-1 bg-components-card-bg border-[0.5px] border-solid border-components-card-border rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
+        className='group relative col-span-1 bg-white border-[0.5px] border-solid border-transparent rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
         data-disable-nprogress={true}
         onClick={(e) => {
           e.preventDefault()
@@ -129,8 +129,8 @@ const DatasetCard = ({
             <Folder className='w-5 h-5 text-[#444CE7]' />
           </div>
           <div className='grow w-0 py-[1px]'>
-            <div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
-              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100 text-text-tertiary')} title={dataset.name}>{dataset.name}</div>
+            <div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
+              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
               {!dataset.embedding_available && (
                 <Tooltip
                   popupContent={t('dataset.unavailableTip')}
@@ -139,7 +139,7 @@ const DatasetCard = ({
                 </Tooltip>
               )}
             </div>
-            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-text-tertiary'>
+            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
               <div
                 className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
                 title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
@@ -162,7 +162,7 @@ const DatasetCard = ({
         </div>
         <div
           className={cn(
-            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-text-tertiary group-hover:line-clamp-2 group-hover:max-h-[36px]',
+            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-gray-500 group-hover:line-clamp-2 group-hover:max-h-[36px]',
             tags.length ? 'line-clamp-2' : 'line-clamp-4',
             !dataset.embedding_available && 'opacity-50 hover:opacity-100',
           )}

+ 0 - 45
web/app/components/app-sidebar/dataset-info.tsx

@@ -1,45 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import AppIcon from '../base/app-icon'
-
-const DatasetSvg = <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-  <path fillRule="evenodd" clipRule="evenodd" d="M0.833497 5.13481C0.833483 4.69553 0.83347 4.31654 0.858973 4.0044C0.88589 3.67495 0.94532 3.34727 1.10598 3.03195C1.34567 2.56155 1.72812 2.17909 2.19852 1.93941C2.51384 1.77875 2.84152 1.71932 3.17097 1.6924C3.48312 1.6669 3.86209 1.66691 4.30137 1.66693L7.62238 1.66684C8.11701 1.66618 8.55199 1.66561 8.95195 1.80356C9.30227 1.92439 9.62134 2.12159 9.88607 2.38088C10.1883 2.67692 10.3823 3.06624 10.603 3.50894L11.3484 5.00008H14.3679C15.0387 5.00007 15.5924 5.00006 16.0434 5.03691C16.5118 5.07518 16.9424 5.15732 17.3468 5.36339C17.974 5.68297 18.4839 6.19291 18.8035 6.82011C19.0096 7.22456 19.0917 7.65515 19.13 8.12356C19.1668 8.57455 19.1668 9.12818 19.1668 9.79898V13.5345C19.1668 14.2053 19.1668 14.7589 19.13 15.2099C19.0917 15.6784 19.0096 16.1089 18.8035 16.5134C18.4839 17.1406 17.974 17.6505 17.3468 17.9701C16.9424 18.1762 16.5118 18.2583 16.0434 18.2966C15.5924 18.3334 15.0387 18.3334 14.3679 18.3334H5.63243C4.96163 18.3334 4.40797 18.3334 3.95698 18.2966C3.48856 18.2583 3.05798 18.1762 2.65353 17.9701C2.02632 17.6505 1.51639 17.1406 1.19681 16.5134C0.990734 16.1089 0.908597 15.6784 0.870326 15.2099C0.833478 14.7589 0.833487 14.2053 0.833497 13.5345V5.13481ZM7.51874 3.33359C8.17742 3.33359 8.30798 3.34447 8.4085 3.37914C8.52527 3.41942 8.63163 3.48515 8.71987 3.57158C8.79584 3.64598 8.86396 3.7579 9.15852 4.34704L9.48505 5.00008L2.50023 5.00008C2.50059 4.61259 2.50314 4.34771 2.5201 4.14012C2.5386 3.91374 2.57 3.82981 2.59099 3.7886C2.67089 3.6318 2.79837 3.50432 2.95517 3.42442C2.99638 3.40343 3.08031 3.37203 3.30669 3.35353C3.54281 3.33424 3.85304 3.33359 4.3335 3.33359H7.51874Z" fill="#444CE7" />
-</svg>
-
-type Props = {
-  isExternal?: boolean
-  name: string
-  description: string
-  expand: boolean
-  extraInfo?: React.ReactNode
-}
-
-const DatasetInfo: FC<Props> = ({
-  name,
-  description,
-  isExternal,
-  expand,
-  extraInfo,
-}) => {
-  const { t } = useTranslation()
-  return (
-    <div className='pl-1 pt-1'>
-      <div className='flex-shrink-0 mr-3'>
-        <AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />
-      </div>
-      {expand && (
-        <div className='mt-2'>
-          <div className='system-md-semibold text-text-secondary'>
-            {name}
-          </div>
-          <div className='mt-1 text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : t('dataset.localDocs')}</div>
-          <div className='my-3  system-xs-regular text-text-tertiary first-letter:capitalize'>{description}</div>
-        </div>
-      )}
-      {extraInfo}
-    </div>
-  )
-}
-export default React.memo(DatasetInfo)

+ 11 - 19
web/app/components/app-sidebar/index.tsx

@@ -1,15 +1,15 @@
 import React, { useEffect } from 'react'
 import { useShallow } from 'zustand/react/shallow'
-import { RiLayoutRight2Line } from '@remixicon/react'
-import { LayoutRight2LineMod } from '../base/icons/src/public/knowledge'
 import NavLink from './navLink'
 import type { NavIcon } from './navLink'
 import AppBasic from './basic'
 import AppInfo from './app-info'
-import DatasetInfo from './dataset-info'
 import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import {
+  AlignLeft01,
+  AlignRight01,
+} from '@/app/components/base/icons/src/vender/line/layout'
 import { useStore as useAppStore } from '@/app/components/app/store'
-import cn from '@/utils/classnames'
 
 export type IAppDetailNavProps = {
   iconType?: 'app' | 'dataset' | 'notion'
@@ -63,16 +63,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
         {iconType === 'app' && (
           <AppInfo expand={expand} />
         )}
-        {iconType === 'dataset' && (
-          <DatasetInfo
-            name={title}
-            description={desc}
-            isExternal={isExternal}
-            expand={expand}
-            extraInfo={extraInfo && extraInfo(appSidebarExpand)}
-          />
-        )}
-        {!['app', 'dataset'].includes(iconType) && (
+        {iconType !== 'app' && (
           <AppBasic
             mode={appSidebarExpand}
             iconType={iconType}
@@ -84,9 +75,9 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
           />
         )}
       </div>
-      <div className='px-4'>
-        <div className={cn('mt-1 mx-auto h-[1px] bg-divider-subtle', !expand && 'w-6')} />
-      </div>
+      {!expand && (
+        <div className='mt-1 mx-auto w-6 h-[1px] bg-divider-subtle' />
+      )}
       <nav
         className={`
           grow space-y-1
@@ -98,6 +89,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
             <NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
           )
         })}
+        {extraInfo && extraInfo(appSidebarExpand)}
       </nav>
       {
         !isMobile && (
@@ -113,8 +105,8 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
             >
               {
                 expand
-                  ? <RiLayoutRight2Line className='w-5 h-5 text-components-menu-item-text' />
-                  : <LayoutRight2LineMod className='w-5 h-5 text-components-menu-item-text' />
+                  ? <AlignLeft01 className='w-[14px] h-[14px]' />
+                  : <AlignRight01 className='w-[14px] h-[14px]' />
               }
             </div>
           </div>

+ 6 - 10
web/app/components/app/configuration/dataset-config/settings-modal/index.tsx

@@ -12,7 +12,7 @@ import Divider from '@/app/components/base/divider'
 import Button from '@/app/components/base/button'
 import Input from '@/app/components/base/input'
 import Textarea from '@/app/components/base/textarea'
-import { type DataSet, RerankingModeEnum } from '@/models/datasets'
+import type { DataSet } from '@/models/datasets'
 import { useToastContext } from '@/app/components/base/toast'
 import { updateDatasetSetting } from '@/service/datasets'
 import { useAppContext } from '@/context/app-context'
@@ -111,10 +111,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
     }
     const postRetrievalConfig = ensureRerankModelSelected({
       rerankDefaultModel: rerankDefaultModel!,
-      retrievalConfig: {
-        ...retrievalConfig,
-        reranking_enable: retrievalConfig.reranking_mode === RerankingModeEnum.RerankingModel,
-      },
+      retrievalConfig,
       indexMethod,
     })
     try {
@@ -258,8 +255,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
                 disable={!localeCurrentDataset?.embedding_available}
                 value={indexMethod}
                 onChange={v => setIndexMethod(v!)}
-                docForm={currentDataset.doc_form}
-                currentValue={currentDataset.indexing_technique}
+                itemClassName='sm:!w-[280px]'
               />
             </div>
           </div>
@@ -291,7 +287,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
         {/* Retrieval Method Config */}
         {currentDataset?.provider === 'external'
           ? <>
-            <div className={rowClass}><Divider /></div>
+            <div className={rowClass}><Divider/></div>
             <div className={rowClass}>
               <div className={labelClass}>
                 <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.retrievalSetting.title')}</div>
@@ -304,7 +300,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
                 isInRetrievalSetting={true}
               />
             </div>
-            <div className={rowClass}><Divider /></div>
+            <div className={rowClass}><Divider/></div>
             <div className={rowClass}>
               <div className={labelClass}>
                 <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.externalKnowledgeAPI')}</div>
@@ -330,7 +326,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
                 </div>
               </div>
             </div>
-            <div className={rowClass}><Divider /></div>
+            <div className={rowClass}><Divider/></div>
           </>
           : <div className={rowClass}>
             <div className={cn(labelClass, 'w-auto min-w-[168px]')}>

+ 0 - 19
web/app/components/base/app-icon/style.module.css

@@ -1,19 +0,0 @@
-.appIcon.large {
-  @apply w-10 h-10;
-}
-
-.appIcon.small {
-  @apply w-8 h-8;
-}
-
-.appIcon.tiny {
-  @apply w-6 h-6 text-base;
-}
-
-.appIcon.xs {
-  @apply w-3 h-3 text-base;
-}
-
-.appIcon.rounded {
-  @apply rounded-full;
-}

+ 0 - 2
web/app/components/base/auto-height-textarea/common.tsx

@@ -49,6 +49,4 @@ const AutoHeightTextarea = forwardRef<HTMLTextAreaElement, AutoHeightTextareaPro
   },
 )
 
-AutoHeightTextarea.displayName = 'AutoHeightTextarea'
-
 export default AutoHeightTextarea

+ 2 - 4
web/app/components/base/badge.tsx

@@ -3,15 +3,13 @@ import cn from '@/utils/classnames'
 
 type BadgeProps = {
   className?: string
-  text?: string
-  children?: React.ReactNode
+  text: string
   uppercase?: boolean
 }
 
 const Badge = ({
   className,
   text,
-  children,
   uppercase = true,
 }: BadgeProps) => {
   return (
@@ -22,7 +20,7 @@ const Badge = ({
         className,
       )}
     >
-      {children || text}
+      {text}
     </div>
   )
 }

+ 0 - 5
web/app/components/base/checkbox/assets/mixed.svg

@@ -1,5 +0,0 @@
-<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="check">
-<path id="Vector 1" d="M2.5 6H9.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
-</g>
-</svg>

+ 0 - 10
web/app/components/base/checkbox/index.module.css

@@ -1,10 +0,0 @@
-.mixed {
-  background: var(--color-components-checkbox-bg) url(./assets/mixed.svg) center center no-repeat;
-  background-size: 12px 12px;
-  border: none;
-}
-
-.checked.disabled {
-  background-color: #d0d5dd;
-  border-color: #d0d5dd;
-}

+ 1 - 4
web/app/components/base/checkbox/index.tsx

@@ -1,5 +1,4 @@
 import { RiCheckLine } from '@remixicon/react'
-import s from './index.module.css'
 import cn from '@/utils/classnames'
 
 type CheckboxProps = {
@@ -7,17 +6,15 @@ type CheckboxProps = {
   onCheck?: () => void
   className?: string
   disabled?: boolean
-  mixed?: boolean
 }
 
-const Checkbox = ({ checked, onCheck, className, disabled, mixed }: CheckboxProps) => {
+const Checkbox = ({ checked, onCheck, className, disabled }: CheckboxProps) => {
   if (!checked) {
     return (
       <div
         className={cn(
           'w-4 h-4 rounded-[4px] bg-components-checkbox-bg-unchecked border border-components-checkbox-border hover:bg-components-checkbox-bg-unchecked-hover hover:border-components-checkbox-border-hover shadow-xs cursor-pointer',
           disabled && 'border-components-checkbox-border-disabled bg-components-checkbox-bg-disabled hover:border-components-checkbox-border-disabled hover:bg-components-checkbox-bg-disabled cursor-not-allowed',
-          mixed && s.mixed,
           className,
         )}
         onClick={() => {

+ 1 - 1
web/app/components/base/divider/index.tsx

@@ -22,7 +22,7 @@ const dividerVariants = cva('',
   },
 )
 
-export type DividerProps = {
+type DividerProps = {
   className?: string
   style?: CSSProperties
 } & VariantProps<typeof dividerVariants>

+ 0 - 23
web/app/components/base/divider/with-label.tsx

@@ -1,23 +0,0 @@
-import type { FC } from 'react'
-import type { DividerProps } from '.'
-import Divider from '.'
-import classNames from '@/utils/classnames'
-
-export type DividerWithLabelProps = DividerProps & {
-  label: string
-}
-
-export const DividerWithLabel: FC<DividerWithLabelProps> = (props) => {
-  const { label, className, ...rest } = props
-  return <div
-    className="flex items-center gap-2 my-2"
-  >
-    <Divider {...rest} className={classNames('flex-1', className)} />
-    <span className="text-text-tertiary text-xs">
-      {label}
-    </span>
-    <Divider {...rest} className={classNames('flex-1', className)} />
-  </div>
-}
-
-export default DividerWithLabel

+ 1 - 3
web/app/components/base/drawer/index.tsx

@@ -19,7 +19,6 @@ export type IDrawerProps = {
   onClose: () => void
   onCancel?: () => void
   onOk?: () => void
-  unmount?: boolean
 }
 
 export default function Drawer({
@@ -36,12 +35,11 @@ export default function Drawer({
   onClose,
   onCancel,
   onOk,
-  unmount = false,
 }: IDrawerProps) {
   const { t } = useTranslation()
   return (
     <Dialog
-      unmount={unmount}
+      unmount={false}
       open={isOpen}
       onClose={() => !clickOutsideNotOpen && onClose()}
       className="fixed z-30 inset-0 overflow-y-auto"

+ 5 - 2
web/app/components/base/file-uploader/file-type-icon.tsx

@@ -82,8 +82,11 @@ const FileTypeIcon = ({
   size = 'sm',
   className,
 }: FileTypeIconProps) => {
-  const Icon = FILE_TYPE_ICON_MAP[type]?.component || FILE_TYPE_ICON_MAP[FileAppearanceTypeEnum.document].component
-  const color = FILE_TYPE_ICON_MAP[type]?.color || FILE_TYPE_ICON_MAP[FileAppearanceTypeEnum.document].color
+  const Icon = FILE_TYPE_ICON_MAP[type].component
+  const color = FILE_TYPE_ICON_MAP[type].color
+
+  if (!Icon)
+    return null
 
   return <Icon className={cn('shrink-0', SizeMap[size], color, className)} />
 }

+ 0 - 13
web/app/components/base/icons/assets/public/knowledge/chunk.svg

@@ -1,13 +0,0 @@
-<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group">
-<path id="Vector" d="M2.5 10H0V7.5H2.5V10Z" fill="#676F83"/>
-<path id="Vector_2" d="M6.25 6.25H3.75V3.75H6.25V6.25Z" fill="#676F83"/>
-<path id="Vector_3" d="M2.5 6.25H0V3.75H2.5V6.25Z" fill="#676F83"/>
-<path id="Vector_4" d="M6.25 2.5H3.75V0H6.25V2.5Z" fill="#676F83"/>
-<path id="Vector_5" d="M2.5 2.5H0V0H2.5V2.5Z" fill="#676F83"/>
-<path id="Vector_6" d="M10 2.5H7.5V0H10V2.5Z" fill="#676F83"/>
-<path id="Vector_7" d="M9.58342 7.91663H7.91675V9.58329H9.58342V7.91663Z" fill="#676F83"/>
-<path id="Vector_8" d="M9.58342 4.16663H7.91675V5.83329H9.58342V4.16663Z" fill="#676F83"/>
-<path id="Vector_9" d="M5.83341 7.91663H4.16675V9.58329H5.83341V7.91663Z" fill="#676F83"/>
-</g>
-</svg>

+ 0 - 9
web/app/components/base/icons/assets/public/knowledge/collapse.svg

@@ -1,9 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Icon L">
-<g id="Vector">
-<path d="M2.66602 11.3333H0.666016L3.33268 8.66667L5.99935 11.3333H3.99935L3.99935 14H2.66602L2.66602 11.3333Z" fill="#354052"/>
-<path d="M2.66602 4.66667L2.66602 2L3.99935 2L3.99935 4.66667L5.99935 4.66667L3.33268 7.33333L0.666016 4.66667L2.66602 4.66667Z" fill="#354052"/>
-<path d="M7.33268 2.66667H13.9993V4H7.33268V2.66667ZM7.33268 12H13.9993V13.3333H7.33268V12ZM5.99935 7.33333H13.9993V8.66667H5.99935V7.33333Z" fill="#354052"/>
-</g>
-</g>
-</svg>

File diff ditekan karena terlalu besar
+ 0 - 2
web/app/components/base/icons/assets/public/knowledge/general-type.svg


+ 0 - 5
web/app/components/base/icons/assets/public/knowledge/layout-right-2-line-mod.svg

@@ -1,5 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Icon L">
-<path id="Vector" d="M14.0002 2C14.3684 2 14.6668 2.29848 14.6668 2.66667V13.3333C14.6668 13.7015 14.3684 14 14.0002 14H2.00016C1.63198 14 1.3335 13.7015 1.3335 13.3333V2.66667C1.3335 2.29848 1.63198 2 2.00016 2H14.0002ZM13.3335 3.33333H2.66683V12.6667H13.3335V3.33333ZM14.0002 2.66667V13.3333H10.0002V2.66667H14.0002Z" fill="#354052"/>
-</g>
-</svg>

+ 0 - 7
web/app/components/base/icons/assets/public/knowledge/parent-child-type.svg

@@ -1,7 +0,0 @@
-<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group">
-<path id="Vector" d="M2.70833 3.87501C3.51375 3.87501 4.16666 3.22209 4.16666 2.41668C4.16666 1.61126 3.51375 0.958344 2.70833 0.958344C1.90292 0.958344 1.25 1.61126 1.25 2.41668C1.25 3.22209 1.90292 3.87501 2.70833 3.87501Z" fill="#676F83"/>
-<path id="Vector_2" d="M7.29158 3.87501C8.097 3.87501 8.74992 3.22209 8.74992 2.41668C8.74992 1.61126 8.097 0.958344 7.29158 0.958344C6.48617 0.958344 5.83325 1.61126 5.83325 2.41668C5.83325 3.22209 6.48617 3.87501 7.29158 3.87501Z" fill="#676F83"/>
-<path id="Vector_3" d="M7.29167 4.70835C6.83771 4.70886 6.39118 4.82363 5.99324 5.04208C5.59529 5.26053 5.25874 5.57563 5.01459 5.95835C5.34482 5.9622 5.66011 6.09658 5.89159 6.33215C6.12306 6.56771 6.25191 6.8853 6.24998 7.21555C6.24805 7.5458 6.11551 7.86187 5.8813 8.09472C5.6471 8.32756 5.33026 8.45826 5 8.45826C4.66975 8.45826 4.35291 8.32756 4.1187 8.09472C3.8845 7.86187 3.75195 7.5458 3.75003 7.21555C3.7481 6.8853 3.87695 6.56771 4.10842 6.33215C4.3399 6.09658 4.65519 5.9622 4.98542 5.95835C4.67086 5.46415 4.20432 5.08546 3.656 4.87926C3.10767 4.67306 2.50721 4.6505 1.94496 4.81497C1.3827 4.97944 0.889064 5.32205 0.538306 5.79125C0.187547 6.26045 -0.00135882 6.83086 7.35834e-06 7.41668V10.125C7.35834e-06 10.2355 0.043906 10.3415 0.122046 10.4196C0.200186 10.4978 0.306167 10.5417 0.416674 10.5417H3.33334V9.50001L1.83334 8.37501C1.78957 8.34218 1.75269 8.30105 1.72481 8.25397C1.69693 8.20688 1.6786 8.15477 1.67086 8.1006C1.65523 7.99121 1.6837 7.88008 1.75001 7.79168C1.81631 7.70327 1.91502 7.64483 2.02441 7.6292C2.13381 7.61357 2.24493 7.64204 2.33334 7.70835L3.88875 8.87501H6.11125L7.66667 7.70835C7.75507 7.64204 7.8662 7.61357 7.97559 7.6292C8.08499 7.64483 8.1837 7.70327 8.25 7.79168C8.31631 7.88008 8.34478 7.99121 8.32915 8.1006C8.31352 8.21 8.25507 8.30871 8.16667 8.37501L6.66667 9.50001V10.5417H9.58333C9.69384 10.5417 9.79982 10.4978 9.87796 10.4196C9.9561 10.3415 10 10.2355 10 10.125V7.41668C9.99912 6.69866 9.71349 6.01029 9.20577 5.50257C8.69805 4.99485 8.00969 4.70923 7.29167 4.70835Z" fill="#676F83"/>
-</g>
-</svg>

+ 0 - 13
web/app/components/base/icons/assets/public/knowledge/selection-mod.svg

@@ -1,13 +0,0 @@
-<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group">
-<path id="Vector" d="M2.5 10H0V7.5H2.5V10Z" fill="#676F83"/>
-<path id="Vector_2" d="M6.25 6.25H3.75V3.75H6.25V6.25Z" fill="#676F83"/>
-<path id="Vector_3" d="M2.5 6.25H0V3.75H2.5V6.25Z" fill="#676F83"/>
-<path id="Vector_4" d="M6.25 2.5H3.75V0H6.25V2.5Z" fill="#676F83"/>
-<path id="Vector_5" d="M2.5 2.5H0V0H2.5V2.5Z" fill="#676F83"/>
-<path id="Vector_6" d="M10 2.5H7.5V0H10V2.5Z" fill="#676F83"/>
-<path id="Vector_7" d="M9.58332 7.91663H7.91666V9.58329H9.58332V7.91663Z" fill="#676F83"/>
-<path id="Vector_8" d="M9.58332 4.16663H7.91666V5.83329H9.58332V4.16663Z" fill="#676F83"/>
-<path id="Vector_9" d="M5.83332 7.91663H4.16666V9.58329H5.83332V7.91663Z" fill="#676F83"/>
-</g>
-</svg>

+ 0 - 116
web/app/components/base/icons/src/public/knowledge/Chunk.json

@@ -1,116 +0,0 @@
-{
-	"icon": {
-		"type": "element",
-		"isRootNode": true,
-		"name": "svg",
-		"attributes": {
-			"width": "10",
-			"height": "10",
-			"viewBox": "0 0 10 10",
-			"fill": "none",
-			"xmlns": "http://www.w3.org/2000/svg"
-		},
-		"children": [
-			{
-				"type": "element",
-				"name": "g",
-				"attributes": {
-					"id": "Group"
-				},
-				"children": [
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector",
-							"d": "M2.5 10H0V7.5H2.5V10Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_2",
-							"d": "M6.25 6.25H3.75V3.75H6.25V6.25Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_3",
-							"d": "M2.5 6.25H0V3.75H2.5V6.25Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_4",
-							"d": "M6.25 2.5H3.75V0H6.25V2.5Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_5",
-							"d": "M2.5 2.5H0V0H2.5V2.5Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_6",
-							"d": "M10 2.5H7.5V0H10V2.5Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_7",
-							"d": "M9.58342 7.91663H7.91675V9.58329H9.58342V7.91663Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_8",
-							"d": "M9.58342 4.16663H7.91675V5.83329H9.58342V4.16663Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_9",
-							"d": "M5.83341 7.91663H4.16675V9.58329H5.83341V7.91663Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					}
-				]
-			}
-		]
-	},
-	"name": "Chunk"
-}

+ 0 - 16
web/app/components/base/icons/src/public/knowledge/Chunk.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './Chunk.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'Chunk'
-
-export default Icon

+ 0 - 62
web/app/components/base/icons/src/public/knowledge/Collapse.json

@@ -1,62 +0,0 @@
-{
-	"icon": {
-		"type": "element",
-		"isRootNode": true,
-		"name": "svg",
-		"attributes": {
-			"width": "16",
-			"height": "16",
-			"viewBox": "0 0 16 16",
-			"fill": "none",
-			"xmlns": "http://www.w3.org/2000/svg"
-		},
-		"children": [
-			{
-				"type": "element",
-				"name": "g",
-				"attributes": {
-					"id": "Icon L"
-				},
-				"children": [
-					{
-						"type": "element",
-						"name": "g",
-						"attributes": {
-							"id": "Vector"
-						},
-						"children": [
-							{
-								"type": "element",
-								"name": "path",
-								"attributes": {
-									"d": "M2.66602 11.3333H0.666016L3.33268 8.66667L5.99935 11.3333H3.99935L3.99935 14H2.66602L2.66602 11.3333Z",
-									"fill": "currentColor"
-								},
-								"children": []
-							},
-							{
-								"type": "element",
-								"name": "path",
-								"attributes": {
-									"d": "M2.66602 4.66667L2.66602 2L3.99935 2L3.99935 4.66667L5.99935 4.66667L3.33268 7.33333L0.666016 4.66667L2.66602 4.66667Z",
-									"fill": "currentColor"
-								},
-								"children": []
-							},
-							{
-								"type": "element",
-								"name": "path",
-								"attributes": {
-									"d": "M7.33268 2.66667H13.9993V4H7.33268V2.66667ZM7.33268 12H13.9993V13.3333H7.33268V12ZM5.99935 7.33333H13.9993V8.66667H5.99935V7.33333Z",
-									"fill": "currentColor"
-								},
-								"children": []
-							}
-						]
-					}
-				]
-			}
-		]
-	},
-	"name": "Collapse"
-}

+ 0 - 16
web/app/components/base/icons/src/public/knowledge/Collapse.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './Collapse.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'Collapse'
-
-export default Icon

File diff ditekan karena terlalu besar
+ 0 - 27
web/app/components/base/icons/src/public/knowledge/GeneralType.json


+ 0 - 16
web/app/components/base/icons/src/public/knowledge/GeneralType.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './GeneralType.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'GeneralType'
-
-export default Icon

+ 0 - 36
web/app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.json

@@ -1,36 +0,0 @@
-{
-	"icon": {
-		"type": "element",
-		"isRootNode": true,
-		"name": "svg",
-		"attributes": {
-			"width": "16",
-			"height": "16",
-			"viewBox": "0 0 16 16",
-			"fill": "none",
-			"xmlns": "http://www.w3.org/2000/svg"
-		},
-		"children": [
-			{
-				"type": "element",
-				"name": "g",
-				"attributes": {
-					"id": "Icon L"
-				},
-				"children": [
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector",
-							"d": "M14.0002 2C14.3684 2 14.6668 2.29848 14.6668 2.66667V13.3333C14.6668 13.7015 14.3684 14 14.0002 14H2.00016C1.63198 14 1.3335 13.7015 1.3335 13.3333V2.66667C1.3335 2.29848 1.63198 2 2.00016 2H14.0002ZM13.3335 3.33333H2.66683V12.6667H13.3335V3.33333ZM14.0002 2.66667V13.3333H10.0002V2.66667H14.0002Z",
-							"fill": "currentColor"
-						},
-						"children": []
-					}
-				]
-			}
-		]
-	},
-	"name": "LayoutRight2LineMod"
-}

+ 0 - 16
web/app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './LayoutRight2LineMod.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'LayoutRight2LineMod'
-
-export default Icon

+ 0 - 56
web/app/components/base/icons/src/public/knowledge/ParentChildType.json

@@ -1,56 +0,0 @@
-{
-	"icon": {
-		"type": "element",
-		"isRootNode": true,
-		"name": "svg",
-		"attributes": {
-			"width": "10",
-			"height": "11",
-			"viewBox": "0 0 10 11",
-			"fill": "none",
-			"xmlns": "http://www.w3.org/2000/svg"
-		},
-		"children": [
-			{
-				"type": "element",
-				"name": "g",
-				"attributes": {
-					"id": "Group"
-				},
-				"children": [
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector",
-							"d": "M2.70833 3.87501C3.51375 3.87501 4.16666 3.22209 4.16666 2.41668C4.16666 1.61126 3.51375 0.958344 2.70833 0.958344C1.90292 0.958344 1.25 1.61126 1.25 2.41668C1.25 3.22209 1.90292 3.87501 2.70833 3.87501Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_2",
-							"d": "M7.29158 3.87501C8.097 3.87501 8.74992 3.22209 8.74992 2.41668C8.74992 1.61126 8.097 0.958344 7.29158 0.958344C6.48617 0.958344 5.83325 1.61126 5.83325 2.41668C5.83325 3.22209 6.48617 3.87501 7.29158 3.87501Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_3",
-							"d": "M7.29167 4.70835C6.83771 4.70886 6.39118 4.82363 5.99324 5.04208C5.59529 5.26053 5.25874 5.57563 5.01459 5.95835C5.34482 5.9622 5.66011 6.09658 5.89159 6.33215C6.12306 6.56771 6.25191 6.8853 6.24998 7.21555C6.24805 7.5458 6.11551 7.86187 5.8813 8.09472C5.6471 8.32756 5.33026 8.45826 5 8.45826C4.66975 8.45826 4.35291 8.32756 4.1187 8.09472C3.8845 7.86187 3.75195 7.5458 3.75003 7.21555C3.7481 6.8853 3.87695 6.56771 4.10842 6.33215C4.3399 6.09658 4.65519 5.9622 4.98542 5.95835C4.67086 5.46415 4.20432 5.08546 3.656 4.87926C3.10767 4.67306 2.50721 4.6505 1.94496 4.81497C1.3827 4.97944 0.889064 5.32205 0.538306 5.79125C0.187547 6.26045 -0.00135882 6.83086 7.35834e-06 7.41668V10.125C7.35834e-06 10.2355 0.043906 10.3415 0.122046 10.4196C0.200186 10.4978 0.306167 10.5417 0.416674 10.5417H3.33334V9.50001L1.83334 8.37501C1.78957 8.34218 1.75269 8.30105 1.72481 8.25397C1.69693 8.20688 1.6786 8.15477 1.67086 8.1006C1.65523 7.99121 1.6837 7.88008 1.75001 7.79168C1.81631 7.70327 1.91502 7.64483 2.02441 7.6292C2.13381 7.61357 2.24493 7.64204 2.33334 7.70835L3.88875 8.87501H6.11125L7.66667 7.70835C7.75507 7.64204 7.8662 7.61357 7.97559 7.6292C8.08499 7.64483 8.1837 7.70327 8.25 7.79168C8.31631 7.88008 8.34478 7.99121 8.32915 8.1006C8.31352 8.21 8.25507 8.30871 8.16667 8.37501L6.66667 9.50001V10.5417H9.58333C9.69384 10.5417 9.79982 10.4978 9.87796 10.4196C9.9561 10.3415 10 10.2355 10 10.125V7.41668C9.99912 6.69866 9.71349 6.01029 9.20577 5.50257C8.69805 4.99485 8.00969 4.70923 7.29167 4.70835Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					}
-				]
-			}
-		]
-	},
-	"name": "ParentChildType"
-}

+ 0 - 16
web/app/components/base/icons/src/public/knowledge/ParentChildType.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './ParentChildType.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'ParentChildType'
-
-export default Icon

+ 0 - 116
web/app/components/base/icons/src/public/knowledge/SelectionMod.json

@@ -1,116 +0,0 @@
-{
-	"icon": {
-		"type": "element",
-		"isRootNode": true,
-		"name": "svg",
-		"attributes": {
-			"width": "10",
-			"height": "10",
-			"viewBox": "0 0 10 10",
-			"fill": "none",
-			"xmlns": "http://www.w3.org/2000/svg"
-		},
-		"children": [
-			{
-				"type": "element",
-				"name": "g",
-				"attributes": {
-					"id": "Group"
-				},
-				"children": [
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector",
-							"d": "M2.5 10H0V7.5H2.5V10Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_2",
-							"d": "M6.25 6.25H3.75V3.75H6.25V6.25Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_3",
-							"d": "M2.5 6.25H0V3.75H2.5V6.25Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_4",
-							"d": "M6.25 2.5H3.75V0H6.25V2.5Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_5",
-							"d": "M2.5 2.5H0V0H2.5V2.5Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_6",
-							"d": "M10 2.5H7.5V0H10V2.5Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_7",
-							"d": "M9.58332 7.91663H7.91666V9.58329H9.58332V7.91663Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_8",
-							"d": "M9.58332 4.16663H7.91666V5.83329H9.58332V4.16663Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					},
-					{
-						"type": "element",
-						"name": "path",
-						"attributes": {
-							"id": "Vector_9",
-							"d": "M5.83332 7.91663H4.16666V9.58329H5.83332V7.91663Z",
-							"fill": "#676F83"
-						},
-						"children": []
-					}
-				]
-			}
-		]
-	},
-	"name": "SelectionMod"
-}

+ 0 - 16
web/app/components/base/icons/src/public/knowledge/SelectionMod.tsx

@@ -1,16 +0,0 @@
-// GENERATE BY script
-// DON NOT EDIT IT MANUALLY
-
-import * as React from 'react'
-import data from './SelectionMod.json'
-import IconBase from '@/app/components/base/icons/IconBase'
-import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
-
-const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
-  props,
-  ref,
-) => <IconBase {...props} ref={ref} data={data as IconData} />)
-
-Icon.displayName = 'SelectionMod'
-
-export default Icon

+ 0 - 6
web/app/components/base/icons/src/public/knowledge/index.ts

@@ -1,6 +0,0 @@
-export { default as Chunk } from './Chunk'
-export { default as Collapse } from './Collapse'
-export { default as GeneralType } from './GeneralType'
-export { default as LayoutRight2LineMod } from './LayoutRight2LineMod'
-export { default as ParentChildType } from './ParentChildType'
-export { default as SelectionMod } from './SelectionMod'

+ 1 - 1
web/app/components/base/icons/src/vender/features/index.ts

@@ -1,6 +1,5 @@
 export { default as Citations } from './Citations'
 export { default as ContentModeration } from './ContentModeration'
-export { default as Document } from './Document'
 export { default as FolderUpload } from './FolderUpload'
 export { default as LoveMessage } from './LoveMessage'
 export { default as MessageFast } from './MessageFast'
@@ -8,3 +7,4 @@ export { default as Microphone01 } from './Microphone01'
 export { default as TextToAudio } from './TextToAudio'
 export { default as VirtualAssistant } from './VirtualAssistant'
 export { default as Vision } from './Vision'
+export { default as Document } from './Document'

+ 0 - 86
web/app/components/base/input-number/index.tsx

@@ -1,86 +0,0 @@
-import type { FC } from 'react'
-import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
-import Input, { type InputProps } from '../input'
-import classNames from '@/utils/classnames'
-
-export type InputNumberProps = {
-  unit?: string
-  value?: number
-  onChange: (value?: number) => void
-  amount?: number
-  size?: 'sm' | 'md'
-  max?: number
-  min?: number
-  defaultValue?: number
-} & Omit<InputProps, 'value' | 'onChange' | 'size' | 'min' | 'max' | 'defaultValue'>
-
-export const InputNumber: FC<InputNumberProps> = (props) => {
-  const { unit, className, onChange, amount = 1, value, size = 'md', max, min, defaultValue, ...rest } = props
-
-  const isValidValue = (v: number) => {
-    if (max && v > max)
-      return false
-    if (min && v < min)
-      return false
-    return true
-  }
-
-  const inc = () => {
-    if (value === undefined) {
-      onChange(defaultValue)
-      return
-    }
-    const newValue = value + amount
-    if (!isValidValue(newValue))
-      return
-    onChange(newValue)
-  }
-  const dec = () => {
-    if (value === undefined) {
-      onChange(defaultValue)
-      return
-    }
-    const newValue = value - amount
-    if (!isValidValue(newValue))
-      return
-    onChange(newValue)
-  }
-
-  return <div className='flex'>
-    <Input {...rest}
-      // disable default controller
-      type='text'
-      className={classNames('rounded-r-none', className)}
-      value={value}
-      max={max}
-      min={min}
-      onChange={(e) => {
-        if (e.target.value === '')
-          onChange(undefined)
-
-        const parsed = Number(e.target.value)
-        if (Number.isNaN(parsed))
-          return
-
-        if (!isValidValue(parsed))
-          return
-        onChange(parsed)
-      }}
-      unit={unit}
-    />
-    <div className='flex flex-col bg-components-input-bg-normal rounded-r-md border-l border-divider-subtle text-text-tertiary focus:shadow-xs'>
-      <button onClick={inc} className={classNames(
-        size === 'sm' ? 'pt-1' : 'pt-1.5',
-        'px-1.5 hover:bg-components-input-bg-hover',
-      )}>
-        <RiArrowUpSLine className='size-3' />
-      </button>
-      <button onClick={dec} className={classNames(
-        size === 'sm' ? 'pb-1' : 'pb-1.5',
-        'px-1.5 hover:bg-components-input-bg-hover',
-      )}>
-        <RiArrowDownSLine className='size-3' />
-      </button>
-    </div>
-  </div>
-}

+ 0 - 62
web/app/components/base/linked-apps-panel/index.tsx

@@ -1,62 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import Link from 'next/link'
-import { useTranslation } from 'react-i18next'
-import { RiArrowRightUpLine } from '@remixicon/react'
-import cn from '@/utils/classnames'
-import AppIcon from '@/app/components/base/app-icon'
-import type { RelatedApp } from '@/models/datasets'
-
-type ILikedItemProps = {
-  appStatus?: boolean
-  detail: RelatedApp
-  isMobile: boolean
-}
-
-const appTypeMap = {
-  'chat': 'Chatbot',
-  'completion': 'Completion',
-  'agent-chat': 'Agent',
-  'advanced-chat': 'Chatflow',
-  'workflow': 'Workflow',
-}
-
-const LikedItem = ({
-  detail,
-  isMobile,
-}: ILikedItemProps) => {
-  return (
-    <Link className={cn('group/link-item flex items-center justify-between w-full h-8 rounded-lg hover:bg-state-base-hover cursor-pointer px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
-      <div className='flex items-center'>
-        <div className={cn('relative w-6 h-6 rounded-md')}>
-          <AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
-        </div>
-        {!isMobile && <div className={cn(' ml-2 truncate system-sm-medium text-text-primary')}>{detail?.name || '--'}</div>}
-      </div>
-      <div className='group-hover/link-item:hidden shrink-0 system-2xs-medium-uppercase text-text-tertiary'>{appTypeMap[detail.mode]}</div>
-      <RiArrowRightUpLine className='hidden group-hover/link-item:block w-4 h-4 text-text-tertiary' />
-    </Link>
-  )
-}
-
-type Props = {
-  relatedApps: RelatedApp[]
-  isMobile: boolean
-}
-
-const LinkedAppsPanel: FC<Props> = ({
-  relatedApps,
-  isMobile,
-}) => {
-  const { t } = useTranslation()
-  return (
-    <div className='p-1 w-[320px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg rounded-xl  backdrop-blur-[5px]'>
-      <div className='mt-1 mb-0.5 pl-2 system-xs-medium-uppercase text-text-tertiary'>{relatedApps.length || '--'} {t('common.datasetMenus.relatedApp')}</div>
-      {relatedApps.map((item, index) => (
-        <LikedItem key={index} detail={item} isMobile={isMobile} />
-      ))}
-    </div>
-  )
-}
-export default React.memo(LinkedAppsPanel)

+ 1 - 1
web/app/components/base/pagination/index.tsx

@@ -8,7 +8,7 @@ import Button from '@/app/components/base/button'
 import Input from '@/app/components/base/input'
 import cn from '@/utils/classnames'
 
-export type Props = {
+type Props = {
   className?: string
   current: number
   onChange: (cur: number) => void

+ 15 - 21
web/app/components/base/param-item/index.tsx

@@ -1,6 +1,5 @@
 'use client'
 import type { FC } from 'react'
-import { InputNumber } from '../input-number'
 import Tooltip from '@/app/components/base/tooltip'
 import Slider from '@/app/components/base/slider'
 import Switch from '@/app/components/base/switch'
@@ -24,44 +23,39 @@ type Props = {
 const ParamItem: FC<Props> = ({ className, id, name, noTooltip, tip, step = 0.1, min = 0, max, value, enable, onChange, hasSwitch, onSwitchChange }) => {
   return (
     <div className={className}>
-      <div className="flex items-center justify-between">
-        <div className="flex items-center h-6">
+      <div className="flex items-center h-8 justify-between">
+        <div className="flex items-center">
           {hasSwitch && (
             <Switch
               size='md'
-              className='mr-2'
               defaultValue={enable}
               onChange={async (val) => {
                 onSwitchChange?.(id, val)
               }}
             />
           )}
-          <span className="mr-1 text-text-secondary system-sm-semibold">{name}</span>
+          <span className="mx-1 text-gray-900 text-[13px] leading-[18px] font-medium">{name}</span>
           {!noTooltip && (
             <Tooltip
               triggerClassName='w-4 h-4 shrink-0'
               popupContent={<div className="w-[200px]">{tip}</div>}
             />
           )}
+
         </div>
+        <div className="flex items-center"></div>
       </div>
-      <div className="mt-1 flex items-center">
-        <div className="mr-3 flex shrink-0 items-center">
-          <InputNumber
-            disabled={!enable}
-            type="number"
-            min={min}
-            max={max}
-            step={step}
-            size='sm'
-            value={value}
-            onChange={(value) => {
-              onChange(id, value)
-            }}
-            className='w-[72px]'
-          />
+      <div className="mt-2 flex items-center">
+        <div className="mr-4 flex shrink-0 items-center">
+          <input disabled={!enable} type="number" min={min} max={max} step={step} className="block w-[48px] h-7 text-xs leading-[18px] rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900  placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600 disabled:opacity-60" value={(value === null || value === undefined) ? '' : value} onChange={(e) => {
+            const value = parseFloat(e.target.value)
+            if (value < min || value > max)
+              return
+
+            onChange(id, value)
+          }} />
         </div>
-        <div className="flex items-center grow">
+        <div className="flex items-center h-7 grow">
           <Slider
             className='w-full'
             disabled={!enable}

+ 9 - 14
web/app/components/base/radio-card/index.tsx

@@ -26,26 +26,24 @@ const RadioCard: FC<Props> = ({
   onChosen = () => { },
   chosenConfig,
   chosenConfigWrapClassName,
-  className,
 }) => {
   return (
     <div
       className={cn(
-        'relative p-3 border-[0.5px] border-components-option-card-option-border bg-components-option-card-option-bg rounded-xl cursor-pointer',
-        isChosen && 'border-[1.5px] bg-components-option-card-option-selected-bg',
-        className,
+        'border border-components-option-card-option-border bg-components-option-card-option-bg rounded-xl hover:shadow-xs cursor-pointer',
+        isChosen && 'bg-components-option-card-option-selected-bg border-components-panel-border shadow-xs',
       )}
     >
-      <div className='flex gap-x-2' onClick={onChosen}>
-        <div className={cn(iconBgClassName, 'shrink-0 flex size-8 justify-center items-center rounded-lg shadow-md')}>
+      <div className='flex py-3 pl-3 pr-4' onClick={onChosen}>
+        <div className={cn(iconBgClassName, 'mr-3 shrink-0 flex w-8 justify-center h-8 items-center rounded-lg')}>
           {icon}
         </div>
         <div className='grow'>
-          <div className='system-sm-semibold text-text-secondary mb-1'>{title}</div>
-          <div className='system-xs-regular text-text-tertiary'>{description}</div>
+          <div className='leading-5 text-sm font-medium text-gray-900'>{title}</div>
+          <div className='leading-[18px] text-xs font-normal text-[#667085]'>{description}</div>
         </div>
         {!noRadio && (
-          <div className='absolute top-3 right-3'>
+          <div className='shrink-0 flex items-center h-8'>
             <div className={cn(
               'w-4 h-4 border border-components-radio-border bg-components-radio-bg shadow-xs rounded-full',
               isChosen && 'border-[5px] border-components-radio-border-checked',
@@ -54,11 +52,8 @@ const RadioCard: FC<Props> = ({
         )}
       </div>
       {((isChosen && chosenConfig) || noRadio) && (
-        <div className='flex gap-x-2 mt-2'>
-          <div className='size-8 shrink-0'></div>
-          <div className={cn(chosenConfigWrapClassName, 'grow')}>
-            {chosenConfig}
-          </div>
+        <div className={cn(chosenConfigWrapClassName, 'p-3 border-t border-gray-200')}>
+          {chosenConfig}
         </div>
       )}
     </div>

+ 85 - 0
web/app/components/base/retry-button/index.tsx

@@ -0,0 +1,85 @@
+'use client'
+import type { FC } from 'react'
+import React, { useEffect, useReducer } from 'react'
+import { useTranslation } from 'react-i18next'
+import useSWR from 'swr'
+import s from './style.module.css'
+import classNames from '@/utils/classnames'
+import Divider from '@/app/components/base/divider'
+import { getErrorDocs, retryErrorDocs } from '@/service/datasets'
+import type { IndexingStatusResponse } from '@/models/datasets'
+
+const WarningIcon = () =>
+  <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000 /svg">
+    <path fillRule="evenodd" clipRule="evenodd" d="M6.40616 0.834307C6.14751 0.719294 5.85222 0.719294 5.59356 0.834307C5.3938 0.923133 5.26403 1.07959 5.17373 1.20708C5.08495 1.33242 4.9899 1.49664 4.88536 1.67723L0.751783 8.81705C0.646828 8.9983 0.551451 9.16302 0.486781 9.3028C0.421056 9.44487 0.349754 9.63584 0.372478 9.85381C0.401884 10.1359 0.549654 10.3922 0.779012 10.5589C0.956259 10.6878 1.15726 10.7218 1.31314 10.7361C1.46651 10.7501 1.65684 10.7501 1.86628 10.7501H10.1334C10.3429 10.7501 10.5332 10.7501 10.6866 10.7361C10.8425 10.7218 11.0435 10.6878 11.2207 10.5589C11.4501 10.3922 11.5978 10.1359 11.6272 9.85381C11.65 9.63584 11.5787 9.44487 11.5129 9.3028C11.4483 9.16303 11.3529 8.99833 11.248 8.81709L7.11436 1.67722C7.00983 1.49663 6.91477 1.33242 6.82599 1.20708C6.73569 1.07959 6.60593 0.923133 6.40616 0.834307ZM6.49988 4.50012C6.49988 4.22398 6.27602 4.00012 5.99988 4.00012C5.72374 4.00012 5.49988 4.22398 5.49988 4.50012V6.50012C5.49988 6.77626 5.72374 7.00012 5.99988 7.00012C6.27602 7.00012 6.49988 6.77626 6.49988 6.50012V4.50012ZM5.99988 8.00012C5.72374 8.00012 5.49988 8.22398 5.49988 8.50012C5.49988 8.77626 5.72374 9.00012 5.99988 9.00012H6.00488C6.28102 9.00012 6.50488 8.77626 6.50488 8.50012C6.50488 8.22398 6.28102 8.00012 6.00488 8.00012H5.99988Z" fill="#F79009" />
+  </svg>
+
+type Props = {
+  datasetId: string
+}
+type IIndexState = {
+  value: string
+}
+type ActionType = 'retry' | 'success' | 'error'
+
+type IAction = {
+  type: ActionType
+}
+const indexStateReducer = (state: IIndexState, action: IAction) => {
+  const actionMap = {
+    retry: 'retry',
+    success: 'success',
+    error: 'error',
+  }
+
+  return {
+    ...state,
+    value: actionMap[action.type] || state.value,
+  }
+}
+
+const RetryButton: FC<Props> = ({ datasetId }) => {
+  const { t } = useTranslation()
+  const [indexState, dispatch] = useReducer(indexStateReducer, { value: 'success' })
+  const { data: errorDocs } = useSWR({ datasetId }, getErrorDocs)
+
+  const onRetryErrorDocs = async () => {
+    dispatch({ type: 'retry' })
+    const document_ids = errorDocs?.data.map((doc: IndexingStatusResponse) => doc.id) || []
+    const res = await retryErrorDocs({ datasetId, document_ids })
+    if (res.result === 'success')
+      dispatch({ type: 'success' })
+    else
+      dispatch({ type: 'error' })
+  }
+
+  useEffect(() => {
+    if (errorDocs?.total === 0)
+      dispatch({ type: 'success' })
+    else
+      dispatch({ type: 'error' })
+  }, [errorDocs?.total])
+
+  if (indexState.value === 'success')
+    return null
+
+  return (
+    <div className={classNames('inline-flex justify-center items-center gap-2', s.retryBtn)}>
+      <WarningIcon />
+      <span className='flex shrink-0 text-sm text-gray-500'>
+        {errorDocs?.total} {t('dataset.docsFailedNotice')}
+      </span>
+      <Divider type='vertical' className='!h-4' />
+      <span
+        className={classNames(
+          'text-primary-600 font-semibold text-sm cursor-pointer',
+          indexState.value === 'retry' && '!text-gray-500 !cursor-not-allowed',
+        )}
+        onClick={indexState.value === 'error' ? onRetryErrorDocs : undefined}
+      >
+        {t('dataset.retry')}
+      </span>
+    </div>
+  )
+}
+export default RetryButton

+ 4 - 0
web/app/components/base/retry-button/style.module.css

@@ -0,0 +1,4 @@
+.retryBtn {
+    @apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base;
+    @apply border-solid border border-gray-200 text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300;
+}

+ 3 - 4
web/app/components/base/simple-pie-chart/index.tsx

@@ -10,11 +10,10 @@ export type SimplePieChartProps = {
   fill?: string
   stroke?: string
   size?: number
-  animationDuration?: number
   className?: string
 }
 
-const SimplePieChart = ({ percentage = 80, fill = '#fdb022', stroke = '#f79009', size = 12, animationDuration, className }: SimplePieChartProps) => {
+const SimplePieChart = ({ percentage = 80, fill = '#fdb022', stroke = '#f79009', size = 12, className }: SimplePieChartProps) => {
   const option: EChartsOption = useMemo(() => ({
     series: [
       {
@@ -35,7 +34,7 @@ const SimplePieChart = ({ percentage = 80, fill = '#fdb022', stroke = '#f79009',
       {
         type: 'pie',
         radius: '83%',
-        animationDuration: animationDuration ?? 600,
+        animationDuration: 600,
         data: [
           { value: percentage, itemStyle: { color: fill } },
           { value: 100 - percentage, itemStyle: { color: '#fff' } },
@@ -49,7 +48,7 @@ const SimplePieChart = ({ percentage = 80, fill = '#fdb022', stroke = '#f79009',
         cursor: 'default',
       },
     ],
-  }), [stroke, fill, percentage, animationDuration])
+  }), [stroke, fill, percentage])
 
   return (
     <ReactECharts

+ 5 - 8
web/app/components/base/skeleton/index.tsx

@@ -3,7 +3,7 @@ import classNames from '@/utils/classnames'
 
 type SkeletonProps = ComponentProps<'div'>
 
-export const SkeletonContainer: FC<SkeletonProps> = (props) => {
+export const SkeletonContanier: FC<SkeletonProps> = (props) => {
   const { className, children, ...rest } = props
   return (
     <div className={classNames('flex flex-col gap-1', className)} {...rest}>
@@ -30,14 +30,11 @@ export const SkeletonRectangle: FC<SkeletonProps> = (props) => {
   )
 }
 
-export const SkeletonPoint: FC<SkeletonProps> = (props) => {
-  const { className, ...rest } = props
-  return (
-    <div className={classNames('text-text-quaternary text-xs font-medium', className)} {...rest}>·</div>
-  )
-}
+export const SkeletonPoint: FC = () =>
+  <div className='text-text-quaternary text-xs font-medium'>·</div>
+
 /** Usage
- * <SkeletonContainer>
+ * <SkeletonContanier>
  *  <SkeletonRow>
  *    <SkeletonRectangle className="w-96" />
  *    <SkeletonPoint />

+ 0 - 3
web/app/components/base/switch/index.tsx

@@ -64,7 +64,4 @@ const Switch = ({ onChange, size = 'md', defaultValue = false, disabled = false,
     </OriginalSwitch>
   )
 }
-
-Switch.displayName = 'Switch'
-
 export default React.memo(Switch)

+ 24 - 27
web/app/components/base/tag-input/index.tsx

@@ -3,8 +3,8 @@ import type { ChangeEvent, FC, KeyboardEvent } from 'react'
 import { } from 'use-context-selector'
 import { useTranslation } from 'react-i18next'
 import AutosizeInput from 'react-18-input-autosize'
-import { RiAddLine, RiCloseLine } from '@remixicon/react'
 import cn from '@/utils/classnames'
+import { X } from '@/app/components/base/icons/src/vender/line/general'
 import { useToastContext } from '@/app/components/base/toast'
 
 type TagInputProps = {
@@ -75,14 +75,14 @@ const TagInput: FC<TagInputProps> = ({
         (items || []).map((item, index) => (
           <div
             key={item}
-            className={cn('flex items-center mr-1 mt-1 pl-1.5 pr-1 py-1 system-xs-regular text-text-secondary border border-divider-deep bg-components-badge-white-to-dark rounded-md')}
-          >
+            className={cn('flex items-center mr-1 mt-1 px-2 py-1 text-sm text-gray-700 border border-gray-200', isSpecialMode ? 'bg-white rounded-md' : 'rounded-lg')}>
             {item}
             {
               !disableRemove && (
-                <div className='flex items-center justify-center w-4 h-4 cursor-pointer' onClick={() => handleRemove(index)}>
-                  <RiCloseLine className='ml-0.5 w-3.5 h-3.5 text-text-tertiary' />
-                </div>
+                <X
+                  className='ml-0.5 w-3 h-3 text-gray-500 cursor-pointer'
+                  onClick={() => handleRemove(index)}
+                />
               )
             }
           </div>
@@ -90,27 +90,24 @@ const TagInput: FC<TagInputProps> = ({
       }
       {
         !disableAdd && (
-          <div className={cn('flex items-center gap-x-0.5 mt-1 group/tag-add', !isSpecialMode ? 'px-1.5 rounded-md border border-dashed border-divider-deep' : '')}>
-            {!isSpecialMode && !focused && <RiAddLine className='w-3.5 h-3.5 text-text-placeholder group-hover/tag-add:text-text-secondary' />}
-            <AutosizeInput
-              inputClassName={cn('outline-none appearance-none placeholder:text-text-placeholder caret-[#295EFF] group-hover/tag-add:placeholder:text-text-secondary', isSpecialMode ? 'bg-transparent' : '')}
-              className={cn(
-                !isInWorkflow && 'max-w-[300px]',
-                isInWorkflow && 'max-w-[146px]',
-                `
-                py-1 rounded-md overflow-hidden system-xs-regular
-                ${focused && isSpecialMode && 'px-1.5 border border-dashed border-divider-deep'}
-              `)}
-              onFocus={() => setFocused(true)}
-              onBlur={handleBlur}
-              value={value}
-              onChange={(e: ChangeEvent<HTMLInputElement>) => {
-                setValue(e.target.value)
-              }}
-              onKeyDown={handleKeyDown}
-              placeholder={t(placeholder || (isSpecialMode ? 'common.model.params.stop_sequencesPlaceholder' : 'datasetDocuments.segment.addKeyWord'))}
-            />
-          </div>
+          <AutosizeInput
+            inputClassName={cn('outline-none appearance-none placeholder:text-gray-300 caret-primary-600 hover:placeholder:text-gray-400', isSpecialMode ? 'bg-transparent' : '')}
+            className={cn(
+              !isInWorkflow && 'max-w-[300px]',
+              isInWorkflow && 'max-w-[146px]',
+              `
+              mt-1 py-1 rounded-lg border border-transparent text-sm  overflow-hidden
+              ${focused && 'px-2 border !border-dashed !border-gray-200'}
+            `)}
+            onFocus={() => setFocused(true)}
+            onBlur={handleBlur}
+            value={value}
+            onChange={(e: ChangeEvent<HTMLInputElement>) => {
+              setValue(e.target.value)
+            }}
+            onKeyDown={handleKeyDown}
+            placeholder={t(placeholder || (isSpecialMode ? 'common.model.params.stop_sequencesPlaceholder' : 'datasetDocuments.segment.addKeyWord'))}
+          />
         )
       }
     </div>

+ 7 - 12
web/app/components/base/toast/index.tsx

@@ -21,7 +21,6 @@ export type IToastProps = {
   children?: ReactNode
   onClose?: () => void
   className?: string
-  customComponent?: ReactNode
 }
 type IToastContext = {
   notify: (props: IToastProps) => void
@@ -36,7 +35,6 @@ const Toast = ({
   message,
   children,
   className,
-  customComponent,
 }: IToastProps) => {
   const { close } = useToastContext()
   // sometimes message is react node array. Not handle it.
@@ -51,7 +49,8 @@ const Toast = ({
     'top-0',
     'right-0',
   )}>
-    <div className={`absolute inset-0 opacity-40 -z-10 ${(type === 'success' && 'bg-[linear-gradient(92deg,rgba(23,178,106,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
+    <div className={`absolute inset-0 opacity-40 ${
+      (type === 'success' && 'bg-[linear-gradient(92deg,rgba(23,178,106,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
       || (type === 'warning' && 'bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
       || (type === 'error' && 'bg-[linear-gradient(92deg,rgba(240,68,56,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
       || (type === 'info' && 'bg-[linear-gradient(92deg,rgba(11,165,236,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
@@ -64,17 +63,14 @@ const Toast = ({
         {type === 'warning' && <RiAlertFill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-warning-secondary`} aria-hidden="true" />}
         {type === 'info' && <RiInformation2Fill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-accent`} aria-hidden="true" />}
       </div>
-      <div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} flex-col items-start gap-1 flex-grow z-10`}>
-        <div className='flex items-center gap-1'>
-          <div className='text-text-primary system-sm-semibold'>{message}</div>
-          {customComponent}
-        </div>
+      <div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} flex-col items-start gap-1 flex-grow`}>
+        <div className='text-text-primary system-sm-semibold'>{message}</div>
         {children && <div className='text-text-secondary system-xs-regular'>
           {children}
         </div>
         }
       </div>
-      <ActionButton onClick={close}>
+      <ActionButton className='z-[1000]' onClick={close}>
         <RiCloseLine className='w-4 h-4 flex-shrink-0 text-text-tertiary' />
       </ActionButton>
     </div>
@@ -121,8 +117,7 @@ Toast.notify = ({
   message,
   duration,
   className,
-  customComponent,
-}: Pick<IToastProps, 'type' | 'size' | 'message' | 'duration' | 'className' | 'customComponent'>) => {
+}: Pick<IToastProps, 'type' | 'size' | 'message' | 'duration' | 'className'>) => {
   const defaultDuring = (type === 'success' || type === 'info') ? 3000 : 6000
   if (typeof window === 'object') {
     const holder = document.createElement('div')
@@ -138,7 +133,7 @@ Toast.notify = ({
           }
         },
       }}>
-        <Toast type={type} size={size} message={message} duration={duration} className={className} customComponent={customComponent} />
+        <Toast type={type} size={size} message={message} duration={duration} className={className} />
       </ToastContext.Provider>,
     )
     document.body.appendChild(holder)

+ 1 - 3
web/app/components/base/tooltip/index.tsx

@@ -14,7 +14,6 @@ export type TooltipProps = {
   popupContent?: React.ReactNode
   children?: React.ReactNode
   popupClassName?: string
-  noDecoration?: boolean
   offset?: OffsetOptions
   needsDelay?: boolean
   asChild?: boolean
@@ -28,7 +27,6 @@ const Tooltip: FC<TooltipProps> = ({
   popupContent,
   children,
   popupClassName,
-  noDecoration,
   offset,
   asChild = true,
   needsDelay = false,
@@ -98,7 +96,7 @@ const Tooltip: FC<TooltipProps> = ({
       >
         {popupContent && (<div
           className={cn(
-            !noDecoration && 'relative px-3 py-2 system-xs-regular text-text-tertiary bg-components-panel-bg rounded-md shadow-lg break-words',
+            'relative px-3 py-2 system-xs-regular text-text-tertiary bg-components-panel-bg rounded-md shadow-lg break-words',
             popupClassName,
           )}
           onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()}

+ 7 - 12
web/app/components/billing/priority-label/index.tsx

@@ -4,7 +4,6 @@ import {
   DocumentProcessingPriority,
   Plan,
 } from '../type'
-import cn from '@/utils/classnames'
 import { useProviderContext } from '@/context/provider-context'
 import {
   ZapFast,
@@ -12,11 +11,7 @@ import {
 } from '@/app/components/base/icons/src/vender/solid/general'
 import Tooltip from '@/app/components/base/tooltip'
 
-type PriorityLabelProps = {
-  className?: string
-}
-
-const PriorityLabel = ({ className }: PriorityLabelProps) => {
+const PriorityLabel = () => {
   const { t } = useTranslation()
   const { plan } = useProviderContext()
 
@@ -42,18 +37,18 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => {
         }
       </div>
     }>
-      <span className={cn(`
-        shrink-0 flex items-center ml-1 px-1 h-[18px] rounded-[5px] border border-text-accent-secondary
-        text-2xs font-medium text-text-accent-secondary
-      `, className)}>
+      <span className={`
+        flex items-center ml-1 px-[5px] h-[18px] rounded border border-[#C7D7FE]
+        text-[10px] font-medium text-[#3538CD]
+      `}>
         {
           plan.type === Plan.professional && (
-            <ZapNarrow className='mr-0.5 size-3' />
+            <ZapNarrow className='mr-0.5 w-3 h-3' />
           )
         }
         {
           (plan.type === Plan.team || plan.type === Plan.enterprise) && (
-            <ZapFast className='mr-0.5 size-3' />
+            <ZapFast className='mr-0.5 w-3 h-3' />
           )
         }
         {t(`billing.plansCommon.priority.${priority}`)}

+ 0 - 54
web/app/components/datasets/chunk.tsx

@@ -1,54 +0,0 @@
-import type { FC, PropsWithChildren } from 'react'
-import { SelectionMod } from '../base/icons/src/public/knowledge'
-import type { QA } from '@/models/datasets'
-
-export type ChunkLabelProps = {
-  label: string
-  characterCount: number
-}
-
-export const ChunkLabel: FC<ChunkLabelProps> = (props) => {
-  const { label, characterCount } = props
-  return <div className='flex items-center text-text-tertiary text-xs font-medium'>
-    <SelectionMod className='size-[10px]' />
-    <p className='flex gap-2 ml-0.5'><span>
-      {label}
-    </span>
-    <span>
-        ·
-    </span>
-    <span>
-      {`${characterCount} characters`}
-    </span></p>
-  </div>
-}
-
-export type ChunkContainerProps = ChunkLabelProps & PropsWithChildren
-
-export const ChunkContainer: FC<ChunkContainerProps> = (props) => {
-  const { label, characterCount, children } = props
-  return <div className='space-y-2'>
-    <ChunkLabel label={label} characterCount={characterCount} />
-    <div className='text-text-secondary body-md-regular'>
-      {children}
-    </div>
-  </div>
-}
-
-export type QAPreviewProps = {
-  qa: QA
-}
-
-export const QAPreview: FC<QAPreviewProps> = (props) => {
-  const { qa } = props
-  return <div className='flex flex-col gap-y-2'>
-    <div className='flex gap-x-1'>
-      <label className='text-text-tertiary text-[13px] font-medium leading-[20px] shrink-0'>Q</label>
-      <p className='text-text-secondary body-md-regular'>{qa.question}</p>
-    </div>
-    <div className='flex gap-x-1'>
-      <label className='text-text-tertiary text-[13px] font-medium leading-[20px] shrink-0'>A</label>
-      <p className='text-text-secondary body-md-regular'>{qa.answer}</p>
-    </div>
-  </div>
-}

+ 0 - 29
web/app/components/datasets/common/chunking-mode-label.tsx

@@ -1,29 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import Badge from '@/app/components/base/badge'
-import { GeneralType, ParentChildType } from '@/app/components/base/icons/src/public/knowledge'
-
-type Props = {
-  isGeneralMode: boolean
-  isQAMode: boolean
-}
-
-const ChunkingModeLabel: FC<Props> = ({
-  isGeneralMode,
-  isQAMode,
-}) => {
-  const { t } = useTranslation()
-  const TypeIcon = isGeneralMode ? GeneralType : ParentChildType
-
-  return (
-    <Badge>
-      <div className='flex items-center h-full space-x-0.5 text-text-tertiary'>
-        <TypeIcon className='w-3 h-3' />
-        <span className='system-2xs-medium-uppercase'>{isGeneralMode ? `${t('dataset.chunkingMode.general')}${isQAMode ? ' · QA' : ''}` : t('dataset.chunkingMode.parentChild')}</span>
-      </div>
-    </Badge>
-  )
-}
-export default React.memo(ChunkingModeLabel)

+ 0 - 40
web/app/components/datasets/common/document-file-icon.tsx

@@ -1,40 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import FileTypeIcon from '../../base/file-uploader/file-type-icon'
-import type { FileAppearanceType } from '@/app/components/base/file-uploader/types'
-import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
-
-const extendToFileTypeMap: { [key: string]: FileAppearanceType } = {
-  pdf: FileAppearanceTypeEnum.pdf,
-  json: FileAppearanceTypeEnum.document,
-  html: FileAppearanceTypeEnum.document,
-  txt: FileAppearanceTypeEnum.document,
-  markdown: FileAppearanceTypeEnum.markdown,
-  md: FileAppearanceTypeEnum.markdown,
-  xlsx: FileAppearanceTypeEnum.excel,
-  xls: FileAppearanceTypeEnum.excel,
-  csv: FileAppearanceTypeEnum.excel,
-  doc: FileAppearanceTypeEnum.word,
-  docx: FileAppearanceTypeEnum.word,
-}
-
-type Props = {
-  extension?: string
-  name?: string
-  size?: 'sm' | 'lg' | 'md'
-  className?: string
-}
-
-const DocumentFileIcon: FC<Props> = ({
-  extension,
-  name,
-  size = 'md',
-  className,
-}) => {
-  const localExtension = extension?.toLowerCase() || name?.split('.')?.pop()?.toLowerCase()
-  return (
-    <FileTypeIcon type={extendToFileTypeMap[localExtension!] || FileAppearanceTypeEnum.document} size={size} className={className} />
-  )
-}
-export default React.memo(DocumentFileIcon)

+ 0 - 42
web/app/components/datasets/common/document-picker/document-list.tsx

@@ -1,42 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useCallback } from 'react'
-import FileIcon from '../document-file-icon'
-import cn from '@/utils/classnames'
-import type { DocumentItem } from '@/models/datasets'
-
-type Props = {
-  className?: string
-  list: DocumentItem[]
-  onChange: (value: DocumentItem) => void
-}
-
-const DocumentList: FC<Props> = ({
-  className,
-  list,
-  onChange,
-}) => {
-  const handleChange = useCallback((item: DocumentItem) => {
-    return () => onChange(item)
-  }, [onChange])
-
-  return (
-    <div className={cn(className)}>
-      {list.map((item) => {
-        const { id, name, extension } = item
-        return (
-          <div
-            key={id}
-            className='flex items-center h-8 px-2 hover:bg-state-base-hover rounded-lg space-x-2 cursor-pointer'
-            onClick={handleChange(item)}
-          >
-            <FileIcon name={item.name} extension={extension} size='md' />
-            <div className='truncate text-text-secondary text-sm'>{name}</div>
-          </div>
-        )
-      })}
-    </div>
-  )
-}
-
-export default React.memo(DocumentList)

+ 0 - 118
web/app/components/datasets/common/document-picker/index.tsx

@@ -1,118 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useCallback, useState } from 'react'
-import { useBoolean } from 'ahooks'
-import { RiArrowDownSLine } from '@remixicon/react'
-import { useTranslation } from 'react-i18next'
-import FileIcon from '../document-file-icon'
-import DocumentList from './document-list'
-import type { DocumentItem, ParentMode, SimpleDocumentDetail } from '@/models/datasets'
-import { ProcessMode } from '@/models/datasets'
-import {
-  PortalToFollowElem,
-  PortalToFollowElemContent,
-  PortalToFollowElemTrigger,
-} from '@/app/components/base/portal-to-follow-elem'
-import cn from '@/utils/classnames'
-import SearchInput from '@/app/components/base/search-input'
-import { GeneralType, ParentChildType } from '@/app/components/base/icons/src/public/knowledge'
-import { useDocumentList } from '@/service/knowledge/use-document'
-import Loading from '@/app/components/base/loading'
-
-type Props = {
-  datasetId: string
-  value: {
-    name?: string
-    extension?: string
-    processMode?: ProcessMode
-    parentMode?: ParentMode
-  }
-  onChange: (value: SimpleDocumentDetail) => void
-}
-
-const DocumentPicker: FC<Props> = ({
-  datasetId,
-  value,
-  onChange,
-}) => {
-  const { t } = useTranslation()
-  const {
-    name,
-    extension,
-    processMode,
-    parentMode,
-  } = value
-  const [query, setQuery] = useState('')
-
-  const { data } = useDocumentList({
-    datasetId,
-    query: {
-      keyword: query,
-      page: 1,
-      limit: 20,
-    },
-  })
-  const documentsList = data?.data
-  const isParentChild = processMode === ProcessMode.parentChild
-  const TypeIcon = isParentChild ? ParentChildType : GeneralType
-
-  const [open, {
-    set: setOpen,
-    toggle: togglePopup,
-  }] = useBoolean(false)
-  const ArrowIcon = RiArrowDownSLine
-
-  const handleChange = useCallback(({ id }: DocumentItem) => {
-    onChange(documentsList?.find(item => item.id === id) as SimpleDocumentDetail)
-    setOpen(false)
-  }, [documentsList, onChange, setOpen])
-
-  return (
-    <PortalToFollowElem
-      open={open}
-      onOpenChange={setOpen}
-      placement='bottom-start'
-    >
-      <PortalToFollowElemTrigger onClick={togglePopup}>
-        <div className={cn('flex items-center ml-1 px-2 py-0.5 rounded-lg hover:bg-state-base-hover select-none cursor-pointer', open && 'bg-state-base-hover')}>
-          <FileIcon name={name} extension={extension} size='lg' />
-          <div className='flex flex-col items-start ml-1 mr-0.5'>
-            <div className='flex items-center space-x-0.5'>
-              <span className={cn('system-md-semibold')}> {name || '--'}</span>
-              <ArrowIcon className={'h-4 w-4 text-text-primary'} />
-            </div>
-            <div className='flex items-center h-3 text-text-tertiary space-x-0.5'>
-              <TypeIcon className='w-3 h-3' />
-              <span className={cn('system-2xs-medium-uppercase', isParentChild && 'mt-0.5' /* to icon problem cause not ver align */)}>
-                {isParentChild ? t('dataset.chunkingMode.parentChild') : t('dataset.chunkingMode.general')}
-                {isParentChild && ` · ${!parentMode ? '--' : parentMode === 'paragraph' ? t('dataset.parentMode.paragraph') : t('dataset.parentMode.fullDoc')}`}
-              </span>
-            </div>
-          </div>
-        </div>
-      </PortalToFollowElemTrigger>
-      <PortalToFollowElemContent className='z-[11]'>
-        <div className='w-[360px] p-1 pt-2 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]'>
-          <SearchInput value={query} onChange={setQuery} className='mx-1' />
-          {documentsList
-            ? (
-              <DocumentList
-                className='mt-2'
-                list={documentsList.map(d => ({
-                  id: d.id,
-                  name: d.name,
-                  extension: d.data_source_detail_dict?.upload_file?.extension || '',
-                }))}
-                onChange={handleChange}
-              />
-            )
-            : (<div className='mt-2 flex items-center justify-center w-[360px] h-[100px]'>
-              <Loading />
-            </div>)}
-        </div>
-
-      </PortalToFollowElemContent>
-    </PortalToFollowElem>
-  )
-}
-export default React.memo(DocumentPicker)

+ 0 - 82
web/app/components/datasets/common/document-picker/preview-document-picker.tsx

@@ -1,82 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useCallback } from 'react'
-import { useBoolean } from 'ahooks'
-import { RiArrowDownSLine } from '@remixicon/react'
-import { useTranslation } from 'react-i18next'
-import FileIcon from '../document-file-icon'
-import DocumentList from './document-list'
-import {
-  PortalToFollowElem,
-  PortalToFollowElemContent,
-  PortalToFollowElemTrigger,
-} from '@/app/components/base/portal-to-follow-elem'
-import cn from '@/utils/classnames'
-import Loading from '@/app/components/base/loading'
-import type { DocumentItem } from '@/models/datasets'
-
-type Props = {
-  className?: string
-  value: DocumentItem
-  files: DocumentItem[]
-  onChange: (value: DocumentItem) => void
-}
-
-const PreviewDocumentPicker: FC<Props> = ({
-  className,
-  value,
-  files,
-  onChange,
-}) => {
-  const { t } = useTranslation()
-  const { name, extension } = value
-
-  const [open, {
-    set: setOpen,
-    toggle: togglePopup,
-  }] = useBoolean(false)
-  const ArrowIcon = RiArrowDownSLine
-
-  const handleChange = useCallback((item: DocumentItem) => {
-    onChange(item)
-    setOpen(false)
-  }, [onChange, setOpen])
-
-  return (
-    <PortalToFollowElem
-      open={open}
-      onOpenChange={setOpen}
-      placement='bottom-start'
-      offset={4}
-    >
-      <PortalToFollowElemTrigger onClick={togglePopup}>
-        <div className={cn('flex items-center h-6 px-1 rounded-md hover:bg-state-base-hover select-none', open && 'bg-state-base-hover', className)}>
-          <FileIcon name={name} extension={extension} size='md' />
-          <div className='flex flex-col items-start ml-1'>
-            <div className='flex items-center space-x-0.5'>
-              <span className={cn('system-md-semibold max-w-[200px] truncate text-text-primary')}> {name || '--'}</span>
-              <ArrowIcon className={'h-[18px] w-[18px] text-text-primary'} />
-            </div>
-          </div>
-        </div>
-      </PortalToFollowElemTrigger>
-      <PortalToFollowElemContent className='z-[11]'>
-        <div className='w-[392px] p-1 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]'>
-          {files?.length > 1 && <div className='pl-2 flex items-center h-8 system-xs-medium-uppercase text-text-tertiary'>{t('dataset.preprocessDocument', { num: files.length })}</div>}
-          {files?.length > 0
-            ? (
-              <DocumentList
-                list={files}
-                onChange={handleChange}
-              />
-            )
-            : (<div className='mt-2 flex items-center justify-center w-[360px] h-[100px]'>
-              <Loading />
-            </div>)}
-        </div>
-
-      </PortalToFollowElemContent>
-    </PortalToFollowElem>
-  )
-}
-export default React.memo(PreviewDocumentPicker)

+ 0 - 38
web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx

@@ -1,38 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import StatusWithAction from './status-with-action'
-import { useAutoDisabledDocuments, useDocumentEnable, useInvalidDisabledDocument } from '@/service/knowledge/use-document'
-import Toast from '@/app/components/base/toast'
-type Props = {
-  datasetId: string
-}
-
-const AutoDisabledDocument: FC<Props> = ({
-  datasetId,
-}) => {
-  const { t } = useTranslation()
-  const { data, isLoading } = useAutoDisabledDocuments(datasetId)
-  const invalidDisabledDocument = useInvalidDisabledDocument()
-  const documentIds = data?.document_ids
-  const hasDisabledDocument = documentIds && documentIds.length > 0
-  const { mutateAsync: enableDocument } = useDocumentEnable()
-  const handleEnableDocuments = useCallback(async () => {
-    await enableDocument({ datasetId, documentIds })
-    invalidDisabledDocument()
-    Toast.notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
-  }, [])
-  if (!hasDisabledDocument || isLoading)
-    return null
-
-  return (
-    <StatusWithAction
-      type='info'
-      description={t('dataset.documentsDisabled', { num: documentIds?.length })}
-      actionText={t('dataset.enable')}
-      onAction={handleEnableDocuments}
-    />
-  )
-}
-export default React.memo(AutoDisabledDocument)

+ 0 - 69
web/app/components/datasets/common/document-status-with-action/index-failed.tsx

@@ -1,69 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useReducer } from 'react'
-import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
-import StatusWithAction from './status-with-action'
-import { getErrorDocs, retryErrorDocs } from '@/service/datasets'
-import type { IndexingStatusResponse } from '@/models/datasets'
-
-type Props = {
-  datasetId: string
-}
-type IIndexState = {
-  value: string
-}
-type ActionType = 'retry' | 'success' | 'error'
-
-type IAction = {
-  type: ActionType
-}
-const indexStateReducer = (state: IIndexState, action: IAction) => {
-  const actionMap = {
-    retry: 'retry',
-    success: 'success',
-    error: 'error',
-  }
-
-  return {
-    ...state,
-    value: actionMap[action.type] || state.value,
-  }
-}
-
-const RetryButton: FC<Props> = ({ datasetId }) => {
-  const { t } = useTranslation()
-  const [indexState, dispatch] = useReducer(indexStateReducer, { value: 'success' })
-  const { data: errorDocs, isLoading } = useSWR({ datasetId }, getErrorDocs)
-
-  const onRetryErrorDocs = async () => {
-    dispatch({ type: 'retry' })
-    const document_ids = errorDocs?.data.map((doc: IndexingStatusResponse) => doc.id) || []
-    const res = await retryErrorDocs({ datasetId, document_ids })
-    if (res.result === 'success')
-      dispatch({ type: 'success' })
-    else
-      dispatch({ type: 'error' })
-  }
-
-  useEffect(() => {
-    if (errorDocs?.total === 0)
-      dispatch({ type: 'success' })
-    else
-      dispatch({ type: 'error' })
-  }, [errorDocs?.total])
-
-  if (isLoading || indexState.value === 'success')
-    return null
-
-  return (
-    <StatusWithAction
-      type='warning'
-      description={`${errorDocs?.total} ${t('dataset.docsFailedNotice')}`}
-      actionText={t('dataset.retry')}
-      disabled={indexState.value === 'retry'}
-      onAction={indexState.value === 'error' ? onRetryErrorDocs : () => { }}
-    />
-  )
-}
-export default RetryButton

+ 0 - 65
web/app/components/datasets/common/document-status-with-action/status-with-action.tsx

@@ -1,65 +0,0 @@
-'use client'
-import { RiAlertFill, RiCheckboxCircleFill, RiErrorWarningFill, RiInformation2Fill } from '@remixicon/react'
-import type { FC } from 'react'
-import React from 'react'
-import cn from '@/utils/classnames'
-import Divider from '@/app/components/base/divider'
-
-type Status = 'success' | 'error' | 'warning' | 'info'
-type Props = {
-  type?: Status
-  description: string
-  actionText: string
-  onAction: () => void
-  disabled?: boolean
-}
-
-const IconMap = {
-  success: {
-    Icon: RiCheckboxCircleFill,
-    color: 'text-text-success',
-  },
-  error: {
-    Icon: RiErrorWarningFill,
-    color: 'text-text-destructive',
-  },
-  warning: {
-    Icon: RiAlertFill,
-    color: 'text-text-warning-secondary',
-  },
-  info: {
-    Icon: RiInformation2Fill,
-    color: 'text-text-accent',
-  },
-}
-
-const getIcon = (type: Status) => {
-  return IconMap[type]
-}
-
-const StatusAction: FC<Props> = ({
-  type = 'info',
-  description,
-  actionText,
-  onAction,
-  disabled,
-}) => {
-  const { Icon, color } = getIcon(type)
-  return (
-    <div className='relative flex items-center h-[34px] rounded-lg pl-2 pr-3 border border-components-panel-border bg-components-panel-bg-blur shadow-xs'>
-      <div className={`absolute inset-0 opacity-40 rounded-lg ${(type === 'success' && 'bg-[linear-gradient(92deg,rgba(23,178,106,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
-        || (type === 'warning' && 'bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
-        || (type === 'error' && 'bg-[linear-gradient(92deg,rgba(240,68,56,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
-        || (type === 'info' && 'bg-[linear-gradient(92deg,rgba(11,165,236,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
-      }`}
-      />
-      <div className='relative z-10 flex h-full items-center space-x-2'>
-        <Icon className={cn('w-4 h-4', color)} />
-        <div className='text-[13px] font-normal text-text-secondary'>{description}</div>
-        <Divider type='vertical' className='!h-4' />
-        <div onClick={onAction} className={cn('text-text-accent font-semibold text-[13px] cursor-pointer', disabled && 'text-text-disabled cursor-not-allowed')}>{actionText}</div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(StatusAction)

+ 14 - 13
web/app/components/datasets/common/economical-retrieval-method-config/index.tsx

@@ -2,11 +2,10 @@
 import type { FC } from 'react'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import Image from 'next/image'
 import RetrievalParamConfig from '../retrieval-param-config'
-import { OptionCard } from '../../create/step-two/option-card'
-import { retrievalIcon } from '../../create/icons'
 import { RETRIEVE_METHOD } from '@/types/app'
+import RadioCard from '@/app/components/base/radio-card'
+import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
 import type { RetrievalConfig } from '@/types/app'
 
 type Props = {
@@ -22,17 +21,19 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
 
   return (
     <div className='space-y-2'>
-      <OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.vector} alt='' />}
+      <RadioCard
+        icon={<HighPriority className='w-4 h-4 text-[#7839EE]' />}
         title={t('dataset.retrieval.invertedIndex.title')}
-        description={t('dataset.retrieval.invertedIndex.description')} isActive
-        activeHeaderClassName='bg-dataset-option-card-purple-gradient'
-      >
-        <RetrievalParamConfig
-          type={RETRIEVE_METHOD.invertedIndex}
-          value={value}
-          onChange={onChange}
-        />
-      </OptionCard>
+        description={t('dataset.retrieval.invertedIndex.description')}
+        noRadio
+        chosenConfig={
+          <RetrievalParamConfig
+            type={RETRIEVE_METHOD.invertedIndex}
+            value={value}
+            onChange={onChange}
+          />
+        }
+      />
     </div>
   )
 }

+ 41 - 48
web/app/components/datasets/common/retrieval-method-config/index.tsx

@@ -2,13 +2,12 @@
 import type { FC } from 'react'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import Image from 'next/image'
 import RetrievalParamConfig from '../retrieval-param-config'
-import { OptionCard } from '../../create/step-two/option-card'
-import Effect from '../../create/assets/option-card-effect-purple.svg'
-import { retrievalIcon } from '../../create/icons'
 import type { RetrievalConfig } from '@/types/app'
 import { RETRIEVE_METHOD } from '@/types/app'
+import RadioCard from '@/app/components/base/radio-card'
+import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
+import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
 import { useProviderContext } from '@/context/provider-context'
 import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
 import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
@@ -17,7 +16,6 @@ import {
   RerankingModeEnum,
   WeightedScoreEnum,
 } from '@/models/datasets'
-import Badge from '@/app/components/base/badge'
 
 type Props = {
   value: RetrievalConfig
@@ -58,72 +56,67 @@ const RetrievalMethodConfig: FC<Props> = ({
   return (
     <div className='space-y-2'>
       {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
-        <OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.vector} alt='' />}
+        <RadioCard
+          icon={<Semantic className='w-4 h-4 text-[#7839EE]' />}
           title={t('dataset.retrieval.semantic_search.title')}
           description={t('dataset.retrieval.semantic_search.description')}
-          isActive={
-            value.search_method === RETRIEVE_METHOD.semantic
-          }
-          onSwitched={() => onChange({
+          isChosen={value.search_method === RETRIEVE_METHOD.semantic}
+          onChosen={() => onChange({
             ...value,
             search_method: RETRIEVE_METHOD.semantic,
           })}
-          effectImg={Effect.src}
-          activeHeaderClassName='bg-dataset-option-card-purple-gradient'
-        >
-          <RetrievalParamConfig
-            type={RETRIEVE_METHOD.semantic}
-            value={value}
-            onChange={onChange}
-          />
-        </OptionCard>
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.semantic}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
       )}
       {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
-        <OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.fullText} alt='' />}
+        <RadioCard
+          icon={<FileSearch02 className='w-4 h-4 text-[#7839EE]' />}
           title={t('dataset.retrieval.full_text_search.title')}
           description={t('dataset.retrieval.full_text_search.description')}
-          isActive={
-            value.search_method === RETRIEVE_METHOD.fullText
-          }
-          onSwitched={() => onChange({
+          isChosen={value.search_method === RETRIEVE_METHOD.fullText}
+          onChosen={() => onChange({
             ...value,
             search_method: RETRIEVE_METHOD.fullText,
           })}
-          effectImg={Effect.src}
-          activeHeaderClassName='bg-dataset-option-card-purple-gradient'
-        >
-          <RetrievalParamConfig
-            type={RETRIEVE_METHOD.fullText}
-            value={value}
-            onChange={onChange}
-          />
-        </OptionCard>
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.fullText}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
       )}
       {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
-        <OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.hybrid} alt='' />}
+        <RadioCard
+          icon={<PatternRecognition className='w-4 h-4 text-[#7839EE]' />}
           title={
             <div className='flex items-center space-x-1'>
               <div>{t('dataset.retrieval.hybrid_search.title')}</div>
-              <Badge text={t('dataset.retrieval.hybrid_search.recommend')!} className='border-text-accent-secondary text-text-accent-secondary ml-1 h-[18px]' uppercase />
+              <div className='flex h-full items-center px-1.5 rounded-md border border-[#E0EAFF] text-xs font-medium text-[#444CE7]'>{t('dataset.retrieval.hybrid_search.recommend')}</div>
             </div>
           }
-          description={t('dataset.retrieval.hybrid_search.description')} isActive={
-            value.search_method === RETRIEVE_METHOD.hybrid
-          }
-          onSwitched={() => onChange({
+          description={t('dataset.retrieval.hybrid_search.description')}
+          isChosen={value.search_method === RETRIEVE_METHOD.hybrid}
+          onChosen={() => onChange({
             ...value,
             search_method: RETRIEVE_METHOD.hybrid,
             reranking_enable: true,
           })}
-          effectImg={Effect.src}
-          activeHeaderClassName='bg-dataset-option-card-purple-gradient'
-        >
-          <RetrievalParamConfig
-            type={RETRIEVE_METHOD.hybrid}
-            value={value}
-            onChange={onChange}
-          />
-        </OptionCard>
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.hybrid}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
       )}
     </div>
   )

+ 10 - 10
web/app/components/datasets/common/retrieval-method-info/index.tsx

@@ -2,11 +2,12 @@
 import type { FC } from 'react'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import Image from 'next/image'
-import { retrievalIcon } from '../../create/icons'
 import type { RetrievalConfig } from '@/types/app'
 import { RETRIEVE_METHOD } from '@/types/app'
 import RadioCard from '@/app/components/base/radio-card'
+import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
+import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
+import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
 
 type Props = {
   value: RetrievalConfig
@@ -14,12 +15,11 @@ type Props = {
 
 export const getIcon = (type: RETRIEVE_METHOD) => {
   return ({
-    [RETRIEVE_METHOD.semantic]: retrievalIcon.vector,
-    [RETRIEVE_METHOD.fullText]: retrievalIcon.fullText,
-    [RETRIEVE_METHOD.hybrid]: retrievalIcon.hybrid,
-    [RETRIEVE_METHOD.invertedIndex]: retrievalIcon.vector,
-    [RETRIEVE_METHOD.keywordSearch]: retrievalIcon.vector,
-  })[type] || retrievalIcon.vector
+    [RETRIEVE_METHOD.semantic]: Semantic,
+    [RETRIEVE_METHOD.fullText]: FileSearch02,
+    [RETRIEVE_METHOD.hybrid]: PatternRecognition,
+    [RETRIEVE_METHOD.invertedIndex]: HighPriority,
+  })[type] || FileSearch02
 }
 
 const EconomicalRetrievalMethodConfig: FC<Props> = ({
@@ -28,11 +28,11 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
 }) => {
   const { t } = useTranslation()
   const type = value.search_method
-  const icon = <Image className='size-3.5 text-util-colors-purple-purple-600' src={getIcon(type)} alt='' />
+  const Icon = getIcon(type)
   return (
     <div className='space-y-2'>
       <RadioCard
-        icon={icon}
+        icon={<Icon className='w-4 h-4 text-[#7839EE]' />}
         title={t(`dataset.retrieval.${type}.title`)}
         description={t(`dataset.retrieval.${type}.description`)}
         noRadio

+ 18 - 20
web/app/components/datasets/common/retrieval-param-config/index.tsx

@@ -3,9 +3,6 @@ import type { FC } from 'react'
 import React, { useCallback } from 'react'
 import { useTranslation } from 'react-i18next'
 
-import Image from 'next/image'
-import ProgressIndicator from '../../create/assets/progress-indicator.svg'
-import Reranking from '../../create/assets/rerank.svg'
 import cn from '@/utils/classnames'
 import TopKItem from '@/app/components/base/param-item/top-k-item'
 import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
@@ -23,7 +20,6 @@ import {
 } from '@/models/datasets'
 import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
 import Toast from '@/app/components/base/toast'
-import RadioCard from '@/app/components/base/radio-card'
 
 type Props = {
   type: RETRIEVE_METHOD
@@ -120,7 +116,7 @@ const RetrievalParamConfig: FC<Props> = ({
     <div>
       {!isEconomical && !isHybridSearch && (
         <div>
-          <div className='flex items-center space-x-2 mb-2'>
+          <div className='flex h-8 items-center text-[13px] font-medium text-gray-900 space-x-2'>
             {canToggleRerankModalEnable && (
               <div
                 className='flex items-center'
@@ -140,7 +136,7 @@ const RetrievalParamConfig: FC<Props> = ({
               </div>
             )}
             <div className='flex items-center'>
-              <span className='mr-0.5 system-sm-semibold text-text-secondary'>{t('common.modelProvider.rerankModel.key')}</span>
+              <span className='mr-0.5'>{t('common.modelProvider.rerankModel.key')}</span>
               <Tooltip
                 popupContent={
                   <div className="w-[200px]">{t('common.modelProvider.rerankModel.tip')}</div>
@@ -167,7 +163,7 @@ const RetrievalParamConfig: FC<Props> = ({
       )}
       {
         !isHybridSearch && (
-          <div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-4')}>
+          <div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-6')}>
             <TopKItem
               className='grow'
               value={value.top_k}
@@ -205,22 +201,24 @@ const RetrievalParamConfig: FC<Props> = ({
       {
         isHybridSearch && (
           <>
-            <div className='flex gap-2 mb-4'>
+            <div className='flex items-center justify-between'>
               {
                 rerankingModeOptions.map(option => (
-                  <RadioCard
+                  <div
                     key={option.value}
-                    isChosen={value.reranking_mode === option.value}
-                    onChosen={() => handleChangeRerankMode(option.value)}
-                    icon={<Image src={
-                      option.value === RerankingModeEnum.WeightedScore
-                        ? ProgressIndicator
-                        : Reranking
-                    } alt=''/>}
-                    title={option.label}
-                    description={option.tips}
-                    className='flex-1'
-                  />
+                    className={cn(
+                      'flex items-center justify-center mb-4 w-[calc((100%-8px)/2)] h-8 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg cursor-pointer system-sm-medium text-text-secondary',
+                      value.reranking_mode === RerankingModeEnum.WeightedScore && option.value === RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
+                      value.reranking_mode !== RerankingModeEnum.WeightedScore && option.value !== RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
+                    )}
+                    onClick={() => handleChangeRerankMode(option.value)}
+                  >
+                    <div className='truncate'>{option.label}</div>
+                    <Tooltip
+                      popupContent={<div className='w-[200px]'>{option.tips}</div>}
+                      triggerClassName='ml-0.5 w-3.5 h-3.5'
+                    />
+                  </div>
                 ))
               }
             </div>

+ 0 - 6
web/app/components/datasets/create/assets/family-mod.svg

@@ -1,6 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M6.18055 6.45828C7.52291 6.45828 8.61111 5.37008 8.61111 4.02772C8.61111 2.68536 7.52291 1.59717 6.18055 1.59717C4.8382 1.59717 3.75 2.68536 3.75 4.02772C3.75 5.37008 4.8382 6.45828 6.18055 6.45828Z" fill="#EF6820"/>
-    <path d="M13.8192 6.45828C15.1616 6.45828 16.2498 5.37008 16.2498 4.02772C16.2498 2.68536 15.1616 1.59717 13.8192 1.59717C12.4769 1.59717 11.3887 2.68536 11.3887 4.02772C11.3887 5.37008 12.4769 6.45828 13.8192 6.45828Z" fill="#EF6820"/>
-    <path d="M13.8193 7.84719C13.0627 7.84805 12.3185 8.03933 11.6552 8.40341C10.992 8.7675 10.4311 9.29267 10.0241 9.93053C10.5745 9.93695 11.1 10.1609 11.4858 10.5535C11.8716 10.9461 12.0864 11.4755 12.0831 12.0259C12.0799 12.5763 11.859 13.1031 11.4687 13.4911C11.0783 13.8792 10.5503 14.097 9.99984 14.097C9.44942 14.097 8.92135 13.8792 8.53101 13.4911C8.14066 13.1031 7.91976 12.5763 7.91655 12.0259C7.91334 11.4755 8.12808 10.9461 8.51387 10.5535C8.89966 10.1609 9.42515 9.93695 9.97554 9.93053C9.45127 9.10686 8.67371 8.47572 7.75983 8.13205C6.84596 7.78839 5.84519 7.75078 4.9081 8.0249C3.97101 8.29902 3.14828 8.87003 2.56368 9.65203C1.97908 10.434 1.66424 11.3847 1.66652 12.3611V16.875C1.66652 17.0591 1.73968 17.2358 1.86991 17.366C2.00015 17.4962 2.17678 17.5694 2.36096 17.5694H7.22207V15.8333L4.72207 13.9583C4.64911 13.9036 4.58765 13.835 4.54118 13.7566C4.49472 13.6781 4.46417 13.5912 4.45127 13.501C4.42522 13.3186 4.47267 13.1334 4.58318 12.9861C4.69369 12.8387 4.8582 12.7413 5.04053 12.7153C5.22285 12.6892 5.40806 12.7367 5.5554 12.8472L8.14776 14.7916H11.8519L14.4443 12.8472C14.5916 12.7367 14.7768 12.6892 14.9592 12.7153C15.1415 12.7413 15.306 12.8387 15.4165 12.9861C15.527 13.1334 15.5745 13.3186 15.5484 13.501C15.5224 13.6833 15.425 13.8478 15.2776 13.9583L12.7776 15.8333V17.5694H17.6387C17.8229 17.5694 17.9995 17.4962 18.1298 17.366C18.26 17.2358 18.3332 17.0591 18.3332 16.875V12.3611C18.3317 11.1644 17.8557 10.0171 17.0095 9.17091C16.1633 8.32471 15.016 7.84867 13.8193 7.84719Z" fill="#EF6820"/>
-    </svg>
-    

+ 0 - 5
web/app/components/datasets/create/assets/file-list-3-fill.svg

@@ -1,5 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="file-list-3-fill">
-<path id="Vector" d="M15.8332 18.3333H4.1665C2.7858 18.3333 1.6665 17.2141 1.6665 15.8333V2.50001C1.6665 2.03977 2.0396 1.66667 2.49984 1.66667H14.1665C14.6268 1.66667 14.9998 2.03977 14.9998 2.50001V12.5H18.3332V15.8333C18.3332 17.2141 17.2139 18.3333 15.8332 18.3333ZM14.9998 14.1667V15.8333C14.9998 16.2936 15.3729 16.6667 15.8332 16.6667C16.2934 16.6667 16.6665 16.2936 16.6665 15.8333V14.1667H14.9998ZM4.99984 5.83334V7.50001H11.6665V5.83334H4.99984ZM4.99984 9.16667V10.8333H11.6665V9.16667H4.99984ZM4.99984 12.5V14.1667H9.1665V12.5H4.99984Z" fill="#1570EF"/>
-</g>
-</svg>

File diff ditekan karena terlalu besar
+ 0 - 1
web/app/components/datasets/create/assets/gold.svg


+ 0 - 5
web/app/components/datasets/create/assets/note-mod.svg

@@ -1,5 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="note-mod">
-<path id="Vector" d="M17.6387 3.05555H2.36095C1.97762 3.05555 1.6665 3.36666 1.6665 3.74999V16.25C1.6665 16.6333 1.97762 16.9444 2.36095 16.9444H17.6387C18.0221 16.9444 18.3332 16.6333 18.3332 16.25V3.74999C18.3332 3.36666 18.0221 3.05555 17.6387 3.05555ZM9.30539 14.1667H5.13873C4.75539 14.1667 4.44428 13.8555 4.44428 13.4722C4.44428 13.0889 4.75539 12.7778 5.13873 12.7778H9.30539C9.68873 12.7778 9.99984 13.0889 9.99984 13.4722C9.99984 13.8555 9.68873 14.1667 9.30539 14.1667ZM14.8609 10.6944H5.13873C4.75539 10.6944 4.44428 10.3833 4.44428 9.99999C4.44428 9.61666 4.75539 9.30555 5.13873 9.30555H14.8609C15.2443 9.30555 15.5554 9.61666 15.5554 9.99999C15.5554 10.3833 15.2443 10.6944 14.8609 10.6944ZM14.8609 7.22221H5.13873C4.75539 7.22221 4.44428 6.9111 4.44428 6.52777C4.44428 6.14443 4.75539 5.83332 5.13873 5.83332H14.8609C15.2443 5.83332 15.5554 6.14443 15.5554 6.52777C15.5554 6.9111 15.2443 7.22221 14.8609 7.22221Z" fill="#1570EF"/>
-</g>
-</svg>

+ 0 - 12
web/app/components/datasets/create/assets/option-card-effect-blue.svg

@@ -1,12 +0,0 @@
-<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Effect" opacity="0.8" filter="url(#filter0_f_1328_28605)">
-<circle cx="32" cy="32" r="28" fill="#444CE7"/>
-</g>
-<defs>
-<filter id="filter0_f_1328_28605" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
-<feFlood flood-opacity="0" result="BackgroundImageFix"/>
-<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
-<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_1328_28605"/>
-</filter>
-</defs>
-</svg>

+ 0 - 12
web/app/components/datasets/create/assets/option-card-effect-orange.svg

@@ -1,12 +0,0 @@
-<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Effect" opacity="0.8" filter="url(#filter0_f_481_16338)">
-<circle cx="32" cy="32" r="28" fill="#EF6820"/>
-</g>
-<defs>
-<filter id="filter0_f_481_16338" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
-<feFlood flood-opacity="0" result="BackgroundImageFix"/>
-<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
-<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_481_16338"/>
-</filter>
-</defs>
-</svg>

+ 0 - 12
web/app/components/datasets/create/assets/option-card-effect-purple.svg

@@ -1,12 +0,0 @@
-<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Effect" opacity="0.8" filter="url(#filter0_f_481_16453)">
-<circle cx="32" cy="32" r="28" fill="#6938EF"/>
-</g>
-<defs>
-<filter id="filter0_f_481_16453" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
-<feFlood flood-opacity="0" result="BackgroundImageFix"/>
-<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
-<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_481_16453"/>
-</filter>
-</defs>
-</svg>

+ 0 - 12
web/app/components/datasets/create/assets/pattern-recognition-mod.svg

@@ -1,12 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M3.93923 18.3333C3.48973 18.3333 3.05032 18.2 2.67657 17.9503C2.30282 17.7006 2.01152 17.3456 1.83951 16.9303C1.66749 16.515 1.62248 16.0581 1.71017 15.6172C1.79787 15.1763 2.01432 14.7714 2.33217 14.4535C2.65002 14.1357 3.05498 13.9192 3.49584 13.8315C3.93671 13.7438 4.39368 13.7889 4.80897 13.9609C5.22425 14.1329 5.5792 14.4242 5.82894 14.7979C6.07867 15.1717 6.21196 15.6111 6.21196 16.0606C6.21196 16.6634 5.97251 17.2414 5.54629 17.6677C5.12007 18.0939 4.542 18.3333 3.93923 18.3333Z" fill="#6938EF"/>
-    <path d="M9.99978 7.72726C9.55028 7.72726 9.11087 7.86056 8.73712 8.11029C8.36337 8.36002 8.07207 8.71497 7.90005 9.13026C7.72804 9.54554 7.68303 10.0025 7.77072 10.4434C7.85842 10.8842 8.07487 11.2892 8.39272 11.6071C8.71056 11.9249 9.11553 12.1414 9.55639 12.229C9.99726 12.3167 10.4542 12.2717 10.8695 12.0997C11.2848 11.9277 11.6398 11.6364 11.8895 11.2627C12.1392 10.8889 12.2725 10.4495 12.2725 9.99999C12.2725 9.39723 12.0331 8.81915 11.6068 8.39293C11.1806 7.96671 10.6025 7.72726 9.99978 7.72726Z" fill="#6938EF"/>
-    <path d="M3.93923 1.66666C3.48973 1.66666 3.05032 1.79995 2.67657 2.04968C2.30282 2.29941 2.01152 2.65436 1.83951 3.06965C1.66749 3.48494 1.62248 3.9419 1.71017 4.38277C1.79787 4.82364 2.01432 5.2286 2.33217 5.54644C2.65002 5.86429 3.05498 6.08075 3.49585 6.16844C3.93671 6.25613 4.39368 6.21113 4.80897 6.03911C5.22425 5.86709 5.57921 5.57579 5.82894 5.20204C6.07867 4.8283 6.21196 4.38889 6.21196 3.93938C6.21196 3.33662 5.97251 2.75854 5.54629 2.33232C5.12007 1.9061 4.542 1.66666 3.93923 1.66666Z" fill="#6938EF"/>
-    <path d="M16.0603 1.66666C15.6108 1.66666 15.1714 1.79995 14.7977 2.04968C14.4239 2.29941 14.1326 2.65436 13.9606 3.06965C13.7886 3.48494 13.7436 3.9419 13.8313 4.38277C13.919 4.82364 14.1354 5.2286 14.4533 5.54644C14.7711 5.86429 15.1761 6.08075 15.6169 6.16844C16.0578 6.25613 16.5148 6.21113 16.9301 6.03911C17.3453 5.86709 17.7003 5.57579 17.95 5.20204C18.1998 4.8283 18.3331 4.38889 18.3331 3.93938C18.3331 3.33662 18.0936 2.75854 17.6674 2.33232C17.2412 1.9061 16.6631 1.66666 16.0603 1.66666Z" fill="#6938EF"/>
-    <path d="M16.0603 13.7879C15.6108 13.7879 15.1714 13.9212 14.7977 14.1709C14.4239 14.4206 14.1326 14.7756 13.9606 15.1909C13.7886 15.6062 13.7436 16.0631 13.8313 16.504C13.919 16.9449 14.1354 17.3498 14.4533 17.6677C14.7711 17.9855 15.1761 18.202 15.6169 18.2897C16.0578 18.3774 16.5148 18.3323 16.9301 18.1603C17.3453 17.9883 17.7003 17.697 17.95 17.3233C18.1998 16.9495 18.3331 16.5101 18.3331 16.0606C18.3331 15.4578 18.0936 14.8798 17.6674 14.4535C17.2412 14.0273 16.6631 13.7879 16.0603 13.7879Z" fill="#6938EF"/>
-    <path d="M6.21196 7.72726H1.6665V12.2727H6.21196V7.72726Z" fill="#6938EF"/>
-    <path d="M18.3331 7.72726H13.7876V12.2727H18.3331V7.72726Z" fill="#6938EF"/>
-    <path d="M12.2725 1.66666H7.72705V6.21211H12.2725V1.66666Z" fill="#6938EF"/>
-    <path d="M12.2725 13.7879H7.72705V18.3333H12.2725V13.7879Z" fill="#6938EF"/>
-    </svg>
-    

+ 0 - 7
web/app/components/datasets/create/assets/piggy-bank-mod.svg

@@ -1,7 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path fill-rule="evenodd" clip-rule="evenodd" d="M7.91672 15.2028V17.9805H6.52783V15.2028H7.91672Z" fill="#444CE7"/>
-    <path fill-rule="evenodd" clip-rule="evenodd" d="M14.1667 15.2028V17.9805H12.7778V15.2028H14.1667Z" fill="#444CE7"/>
-    <path d="M14.1666 2.0083C14.1666 3.54243 12.923 4.78608 11.3889 4.78608C9.85476 4.78608 8.61108 3.54243 8.61108 2.0083L14.1666 2.0083Z" fill="#444CE7"/>
-    <path fill-rule="evenodd" clip-rule="evenodd" d="M5.8864 5.23918C6.9718 4.92907 8.12598 5.30791 8.81883 6.17498H13.1251C16.0015 6.17498 18.3334 8.50683 18.3334 11.3833C18.3334 14.2598 16.0015 16.5916 13.1251 16.5916H7.39252C6.14908 16.5916 4.97062 16.0363 4.1791 15.0773L3.32342 14.0407L1.66675 13.3448V9.93061L3.65692 9.40957L4.44453 8.40703V5.65114L5.8864 5.23918ZM8.61119 8.25831H14.1667V9.64721H8.61119V8.25831ZM6.52786 9.99443C6.52786 10.5697 6.06149 11.0361 5.48619 11.0361C4.91089 11.0361 4.44453 10.5697 4.44453 9.99443C4.44453 9.41915 4.91089 8.95276 5.48619 8.95276C6.06149 8.95276 6.52786 9.41915 6.52786 9.99443Z" fill="#444CE7"/>
-    </svg>
-    

+ 0 - 8
web/app/components/datasets/create/assets/progress-indicator.svg

@@ -1,8 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="progress-indicator">
-<g id="Vector">
-<path d="M18.4029 10.7639H1.59738C1.17572 10.7639 0.833496 11.1061 0.833496 11.5278V16.1111C0.833496 16.5328 1.17572 16.875 1.59738 16.875H18.4029C18.8246 16.875 19.1668 16.5328 19.1668 16.1111V11.5278C19.1668 11.1061 18.8246 10.7639 18.4029 10.7639ZM17.6391 15.3472H10.0002V12.2917H17.6391V15.3472Z" fill="#1570EF"/>
-<path d="M9.716 7.58153C9.78933 7.66174 9.89169 7.70833 10.0002 7.70833C10.1086 7.70833 10.211 7.6625 10.2843 7.58153L13.7218 3.76208C13.8227 3.64979 13.8479 3.48937 13.7868 3.35111C13.7249 3.21361 13.5881 3.125 13.4377 3.125H6.56266C6.41218 3.125 6.27544 3.21361 6.21356 3.35111C6.15245 3.48937 6.17766 3.64979 6.2785 3.76208L9.716 7.58153Z" fill="#1570EF"/>
-</g>
-</g>
-</svg>

+ 0 - 13
web/app/components/datasets/create/assets/rerank.svg

@@ -1,13 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="rerank">
-<g id="Vector">
-<path d="M18.3333 4.58329C18.3333 5.73389 17.4005 6.66663 16.2499 6.66663C15.0993 6.66663 14.1666 5.73389 14.1666 4.58329C14.1666 3.4327 15.0993 2.49996 16.2499 2.49996C17.4005 2.49996 18.3333 3.4327 18.3333 4.58329Z" fill="#0E9384"/>
-<path d="M13.3333 15.4166C13.3333 16.5672 12.4005 17.5 11.2499 17.5C10.0993 17.5 9.16658 16.5672 9.16658 15.4166C9.16658 14.266 10.0993 13.3333 11.2499 13.3333C12.4005 13.3333 13.3333 14.266 13.3333 15.4166Z" fill="#0E9384"/>
-<path d="M12.0833 4.58329C12.0833 5.27365 11.5236 5.83329 10.8333 5.83329C10.1429 5.83329 9.58325 5.27365 9.58325 4.58329C9.58325 3.89294 10.1429 3.33329 10.8333 3.33329C11.5236 3.33329 12.0833 3.89294 12.0833 4.58329Z" fill="#0E9384"/>
-<path d="M17.4999 15.4166C17.4999 16.107 16.9403 16.6666 16.2499 16.6666C15.5596 16.6666 14.9999 16.107 14.9999 15.4166C14.9999 14.7263 15.5596 14.1666 16.2499 14.1666C16.9403 14.1666 17.4999 14.7263 17.4999 15.4166Z" fill="#0E9384"/>
-<path d="M7.49992 15.4166C7.49992 17.0275 6.19408 18.3333 4.58325 18.3333C2.97242 18.3333 1.66659 17.0275 1.66659 15.4166C1.66659 13.8058 2.97242 12.5 4.58325 12.5C6.19408 12.5 7.49992 13.8058 7.49992 15.4166Z" fill="#0E9384"/>
-<path d="M7.49992 4.58329C7.49992 6.19412 6.19408 7.49996 4.58325 7.49996C2.97242 7.49996 1.66659 6.19412 1.66659 4.58329C1.66659 2.97246 2.97242 1.66663 4.58325 1.66663C6.19408 1.66663 7.49992 2.97246 7.49992 4.58329Z" fill="#0E9384"/>
-<path d="M0.833252 9.99996C0.833252 9.53972 1.20635 9.16663 1.66659 9.16663H18.3333C18.7935 9.16663 19.1666 9.53972 19.1666 9.99996C19.1666 10.4602 18.7935 10.8333 18.3333 10.8333H1.66659C1.20635 10.8333 0.833252 10.4602 0.833252 9.99996Z" fill="#0E9384"/>
-</g>
-</g>
-</svg>

+ 0 - 6
web/app/components/datasets/create/assets/research-mod.svg

@@ -1,6 +0,0 @@
-<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M13.6752 4.83333H9.91553V1.07361L13.6752 4.83333Z" fill="#6938EF"/>
-    <path d="M7.2003 13.8611H2.62391C2.53183 13.8611 2.44351 13.8245 2.37839 13.7594C2.31327 13.6943 2.27669 13.606 2.27669 13.5139V12.8195C2.27669 12.7274 2.31327 12.6391 2.37839 12.5739C2.44351 12.5088 2.53183 12.4722 2.62391 12.4722H7.2003C7.33176 11.6964 7.68097 10.9739 8.20725 10.3889H2.62391C2.53183 10.3889 2.44351 10.3523 2.37839 10.2872C2.31327 10.2221 2.27669 10.1338 2.27669 10.0417V9.34724C2.27669 9.25515 2.31327 9.16684 2.37839 9.10172C2.44351 9.0366 2.53183 9.00002 2.62391 9.00002H11.3045C12.3309 9.0003 13.3207 9.38137 14.0822 10.0695V6.22224H9.22114C9.03696 6.22224 8.86032 6.14908 8.73009 6.01884C8.59986 5.88861 8.52669 5.71198 8.52669 5.5278V0.666687H0.887804C0.703626 0.666687 0.526991 0.739851 0.396757 0.870085C0.266524 1.00032 0.193359 1.17695 0.193359 1.36113V16.6389C0.193359 16.8231 0.266524 16.9997 0.396757 17.13C0.526991 17.2602 0.703626 17.3334 0.887804 17.3334H10.61C9.73337 17.224 8.91945 16.8214 8.30046 16.1911C7.68146 15.5607 7.29375 14.7396 7.2003 13.8611ZM2.62391 5.5278H6.09614C6.18823 5.5278 6.27654 5.56438 6.34166 5.6295C6.40678 5.69461 6.44336 5.78293 6.44336 5.87502V6.56947C6.44336 6.66155 6.40678 6.74987 6.34166 6.81499C6.27654 6.88011 6.18823 6.91669 6.09614 6.91669H2.62391C2.53183 6.91669 2.44351 6.88011 2.37839 6.81499C2.31327 6.74987 2.27669 6.66155 2.27669 6.56947V5.87502C2.27669 5.78293 2.31327 5.69461 2.37839 5.6295C2.44351 5.56438 2.53183 5.5278 2.62391 5.5278Z" fill="#6938EF"/>
-    <path d="M15.2678 16.1479L13.6887 14.5688C13.9439 14.1455 14.08 13.661 14.0824 13.1667C14.0824 12.6173 13.9195 12.0802 13.6143 11.6234C13.309 11.1666 12.8752 10.8106 12.3676 10.6004C11.8601 10.3901 11.3016 10.3351 10.7627 10.4423C10.2239 10.5495 9.72893 10.814 9.34045 11.2025C8.95197 11.591 8.68741 12.0859 8.58023 12.6248C8.47305 13.1636 8.52806 13.7221 8.7383 14.2297C8.94855 14.7373 9.30458 15.1711 9.76138 15.4763C10.2182 15.7816 10.7552 15.9445 11.3046 15.9445C11.799 15.9421 12.2834 15.806 12.7067 15.5507L14.2859 17.1299C14.4169 17.2564 14.5923 17.3264 14.7744 17.3248C14.9564 17.3232 15.1306 17.2502 15.2594 17.1214C15.3881 16.9927 15.4612 16.8185 15.4627 16.6364C15.4643 16.4543 15.3943 16.2789 15.2678 16.1479ZM9.91575 13.1667C9.91575 12.892 9.9972 12.6235 10.1498 12.3951C10.3024 12.1667 10.5193 11.9887 10.7731 11.8835C11.0269 11.7784 11.3062 11.7509 11.5756 11.8045C11.845 11.8581 12.0925 11.9904 12.2867 12.1846C12.481 12.3788 12.6132 12.6263 12.6668 12.8957C12.7204 13.1652 12.6929 13.4444 12.5878 13.6982C12.4827 13.952 12.3047 14.1689 12.0763 14.3215C11.8479 14.4741 11.5793 14.5556 11.3046 14.5556C10.9363 14.5556 10.583 14.4093 10.3225 14.1488C10.0621 13.8883 9.91575 13.5351 9.91575 13.1667Z" fill="#6938EF"/>
-    </svg>
-    

+ 0 - 12
web/app/components/datasets/create/assets/selection-mod.svg

@@ -1,12 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M5.83317 18.3334H1.6665V14.1667H5.83317V18.3334Z" fill="#6938EF"/>
-    <path d="M12.0832 12.0834H7.9165V7.91669H12.0832V12.0834Z" fill="#6938EF"/>
-    <path d="M5.83317 12.0834H1.6665V7.91669H5.83317V12.0834Z" fill="#6938EF"/>
-    <path d="M12.0832 5.83335H7.9165V1.66669H12.0832V5.83335Z" fill="#6938EF"/>
-    <path d="M5.83317 5.83335H1.6665V1.66669H5.83317V5.83335Z" fill="#6938EF"/>
-    <path d="M18.3332 5.83335H14.1665V1.66669H18.3332V5.83335Z" fill="#6938EF"/>
-    <path d="M17.6386 14.8611H14.8608V17.6389H17.6386V14.8611Z" fill="#6938EF"/>
-    <path d="M17.6386 8.61115H14.8608V11.3889H17.6386V8.61115Z" fill="#6938EF"/>
-    <path d="M11.3886 14.8611H8.61084V17.6389H11.3886V14.8611Z" fill="#6938EF"/>
-    </svg>
-    

File diff ditekan karena terlalu besar
+ 0 - 1
web/app/components/datasets/create/assets/setting-gear-mod.svg


+ 39 - 13
web/app/components/datasets/create/embedding-process/index.module.css

@@ -14,7 +14,24 @@
   border-radius: 6px;
   overflow: hidden;
 }
-
+.sourceItem.error {
+  background: #FEE4E2;
+}
+.sourceItem.success {
+  background: #D1FADF;
+}
+.progressbar {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  background-color: #B2CCFF;
+}
+.sourceItem .info {
+  display: flex;
+  align-items: center;
+  z-index: 1;
+}
 .sourceItem .info .name {
   font-weight: 500;
   font-size: 12px;
@@ -38,6 +55,13 @@
   color: #05603A;
 }
 
+
+.cost {
+  @apply flex justify-between items-center text-xs text-gray-700;
+}
+.embeddingStatus {
+  @apply flex items-center justify-between text-gray-900 font-medium text-sm mr-2;
+}
 .commonIcon {
   @apply w-3 h-3 mr-1 inline-block align-middle;
 }
@@ -57,33 +81,35 @@
   @apply text-xs font-medium;
 }
 
-.unknownFileIcon {
+.fileIcon {
+  @apply w-4 h-4 mr-1 bg-center bg-no-repeat;
   background-image: url(../assets/unknown.svg);
+  background-size: 16px;
 }
-.csv {
+.fileIcon.csv {
   background-image: url(../assets/csv.svg);
 }
-.docx {
+.fileIcon.docx {
   background-image: url(../assets/docx.svg);
 }
-.xlsx,
-.xls {
+.fileIcon.xlsx,
+.fileIcon.xls {
   background-image: url(../assets/xlsx.svg);
 }
-.pdf {
+.fileIcon.pdf {
   background-image: url(../assets/pdf.svg);
 }
-.html,
-.htm {
+.fileIcon.html,
+.fileIcon.htm {
   background-image: url(../assets/html.svg);
 }
-.md,
-.markdown {
+.fileIcon.md,
+.fileIcon.markdown {
   background-image: url(../assets/md.svg);
 }
-.txt {
+.fileIcon.txt {
   background-image: url(../assets/txt.svg);
 }
-.json {
+.fileIcon.json {
   background-image: url(../assets/json.svg);
 }

+ 55 - 137
web/app/components/datasets/create/embedding-process/index.tsx

@@ -6,44 +6,32 @@ import { useTranslation } from 'react-i18next'
 import { omit } from 'lodash-es'
 import { ArrowRightIcon } from '@heroicons/react/24/solid'
 import {
-  RiCheckboxCircleFill,
   RiErrorWarningFill,
-  RiLoader2Fill,
-  RiTerminalBoxLine,
 } from '@remixicon/react'
-import Image from 'next/image'
-import { indexMethodIcon, retrievalIcon } from '../icons'
-import { IndexingType } from '../step-two'
-import DocumentFileIcon from '../../common/document-file-icon'
+import s from './index.module.css'
 import cn from '@/utils/classnames'
 import { FieldInfo } from '@/app/components/datasets/documents/detail/metadata'
 import Button from '@/app/components/base/button'
 import type { FullDocumentDetail, IndexingStatusResponse, ProcessRuleResponse } from '@/models/datasets'
 import { fetchIndexingStatusBatch as doFetchIndexingStatus, fetchProcessRule } from '@/service/datasets'
-import { DataSourceType, ProcessMode } from '@/models/datasets'
+import { DataSourceType } from '@/models/datasets'
 import NotionIcon from '@/app/components/base/notion-icon'
 import PriorityLabel from '@/app/components/billing/priority-label'
 import { Plan } from '@/app/components/billing/type'
 import { ZapFast } from '@/app/components/base/icons/src/vender/solid/general'
 import UpgradeBtn from '@/app/components/billing/upgrade-btn'
 import { useProviderContext } from '@/context/provider-context'
-import { sleep } from '@/utils'
-import { RETRIEVE_METHOD } from '@/types/app'
 import Tooltip from '@/app/components/base/tooltip'
+import { sleep } from '@/utils'
 
 type Props = {
   datasetId: string
   batchId: string
   documents?: FullDocumentDetail[]
   indexingType?: string
-  retrievalMethod?: string
 }
 
-const RuleDetail: FC<{
-  sourceData?: ProcessRuleResponse
-  indexingType?: string
-  retrievalMethod?: string
-}> = ({ sourceData, indexingType, retrievalMethod }) => {
+const RuleDetail: FC<{ sourceData?: ProcessRuleResponse }> = ({ sourceData }) => {
   const { t } = useTranslation()
 
   const segmentationRuleMap = {
@@ -63,47 +51,29 @@ const RuleDetail: FC<{
       return t('datasetCreation.stepTwo.removeStopwords')
   }
 
-  const isNumber = (value: unknown) => {
-    return typeof value === 'number'
-  }
-
   const getValue = useCallback((field: string) => {
     let value: string | number | undefined = '-'
-    const maxTokens = isNumber(sourceData?.rules?.segmentation?.max_tokens)
-      ? sourceData.rules.segmentation.max_tokens
-      : value
-    const childMaxTokens = isNumber(sourceData?.rules?.subchunk_segmentation?.max_tokens)
-      ? sourceData.rules.subchunk_segmentation.max_tokens
-      : value
     switch (field) {
       case 'mode':
-        value = !sourceData?.mode
-          ? value
-          : sourceData.mode === ProcessMode.general
-            ? (t('datasetDocuments.embedding.custom') as string)
-            : `${t('datasetDocuments.embedding.hierarchical')} · ${sourceData?.rules?.parent_mode === 'paragraph'
-              ? t('dataset.parentMode.paragraph')
-              : t('dataset.parentMode.fullDoc')}`
+        value = sourceData?.mode === 'automatic' ? (t('datasetDocuments.embedding.automatic') as string) : (t('datasetDocuments.embedding.custom') as string)
         break
       case 'segmentLength':
-        value = !sourceData?.mode
-          ? value
-          : sourceData.mode === ProcessMode.general
-            ? maxTokens
-            : `${t('datasetDocuments.embedding.parentMaxTokens')} ${maxTokens}; ${t('datasetDocuments.embedding.childMaxTokens')} ${childMaxTokens}`
+        value = sourceData?.rules?.segmentation?.max_tokens
         break
       default:
-        value = !sourceData?.mode
-          ? value
-          : sourceData?.rules?.pre_processing_rules?.filter(rule =>
-            rule.enabled).map(rule => getRuleName(rule.id)).join(',')
+        value = sourceData?.mode === 'automatic'
+          ? (t('datasetDocuments.embedding.automatic') as string)
+          // eslint-disable-next-line array-callback-return
+          : sourceData?.rules?.pre_processing_rules?.map((rule) => {
+            if (rule.enabled)
+              return getRuleName(rule.id)
+          }).filter(Boolean).join(';')
         break
     }
     return value
-    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [sourceData])
 
-  return <div className='flex flex-col gap-1'>
+  return <div className='flex flex-col pt-8 pb-10 first:mt-0'>
     {Object.keys(segmentationRuleMap).map((field) => {
       return <FieldInfo
         key={field}
@@ -111,43 +81,10 @@ const RuleDetail: FC<{
         displayedValue={String(getValue(field))}
       />
     })}
-    <FieldInfo
-      label={t('datasetCreation.stepTwo.indexMode')}
-      displayedValue={t(`datasetCreation.stepTwo.${indexingType === IndexingType.ECONOMICAL ? 'economical' : 'qualified'}`) as string}
-      valueIcon={
-        <Image
-          className='size-4'
-          src={
-            indexingType === IndexingType.ECONOMICAL
-              ? indexMethodIcon.economical
-              : indexMethodIcon.high_quality
-          }
-          alt=''
-        />
-      }
-    />
-    <FieldInfo
-      label={t('datasetSettings.form.retrievalSetting.title')}
-      // displayedValue={t(`datasetSettings.form.retrievalSetting.${retrievalMethod}`) as string}
-      displayedValue={t(`dataset.retrieval.${indexingType === IndexingType.ECONOMICAL ? 'invertedIndex' : retrievalMethod}.title`) as string}
-      valueIcon={
-        <Image
-          className='size-4'
-          src={
-            retrievalMethod === RETRIEVE_METHOD.fullText
-              ? retrievalIcon.fullText
-              : retrievalMethod === RETRIEVE_METHOD.hybrid
-                ? retrievalIcon.hybrid
-                : retrievalIcon.vector
-          }
-          alt=''
-        />
-      }
-    />
   </div>
 }
 
-const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], indexingType, retrievalMethod }) => {
+const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], indexingType }) => {
   const { t } = useTranslation()
   const { enableBilling, plan } = useProviderContext()
 
@@ -190,7 +127,6 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
   }
 
   useEffect(() => {
-    setIsStopQuery(false)
     startQueryStatus()
     return () => {
       stopQueryStatus()
@@ -210,9 +146,6 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
   const navToDocumentList = () => {
     router.push(`/datasets/${datasetId}/documents`)
   }
-  const navToApiDocs = () => {
-    router.push('/datasets?category=api')
-  }
 
   const isEmbedding = useMemo(() => {
     return indexingStatusBatchDetail.some(indexingStatusDetail => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || ''))
@@ -244,17 +177,13 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
 
     return doc?.data_source_info.notion_page_icon
   }
-  const isSourceEmbedding = (detail: IndexingStatusResponse) =>
-    ['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '')
+  const isSourceEmbedding = (detail: IndexingStatusResponse) => ['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '')
 
   return (
     <>
-      <div className="h-5 flex items-center mb-3">
-        <div className="flex items-center justify-between text-gray-900 font-medium text-sm mr-2">
-          {isEmbedding && <div className='flex items-center'>
-            <RiLoader2Fill className='size-4 mr-1 animate-spin' />
-            {t('datasetDocuments.embedding.processing')}
-          </div>}
+      <div className='h-5 flex items-center mb-5'>
+        <div className={s.embeddingStatus}>
+          {isEmbedding && t('datasetDocuments.embedding.processing')}
           {isEmbeddingCompleted && t('datasetDocuments.embedding.completed')}
         </div>
       </div>
@@ -271,80 +200,69 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
           </div>
         )
       }
-      <div className="flex flex-col gap-0.5 pb-2">
+      <div className={s.progressContainer}>
         {indexingStatusBatchDetail.map(indexingStatusDetail => (
           <div key={indexingStatusDetail.id} className={cn(
-            'relative h-[26px] bg-components-progress-bar-bg rounded-md overflow-hidden',
-            indexingStatusDetail.indexing_status === 'error' && 'bg-state-destructive-hover-alt',
-            // indexingStatusDetail.indexing_status === 'completed' && 's.success',
+            s.sourceItem,
+            indexingStatusDetail.indexing_status === 'error' && s.error,
+            indexingStatusDetail.indexing_status === 'completed' && s.success,
           )}>
             {isSourceEmbedding(indexingStatusDetail) && (
-              <div className="absolute top-0 left-0 h-full min-w-0.5 bg-components-progress-bar-progress border-r-[2px] border-r-components-progress-bar-progress-highlight" style={{ width: `${getSourcePercent(indexingStatusDetail)}%` }} />
+              <div className={s.progressbar} style={{ width: `${getSourcePercent(indexingStatusDetail)}%` }} />
             )}
-            <div className="flex gap-1 pl-[6px] pr-2 h-full items-center z-[1]">
+            <div className={`${s.info} grow`}>
               {getSourceType(indexingStatusDetail.id) === DataSourceType.FILE && (
-                // <div className={cn(
-                //   'shrink-0 marker:size-4 bg-center bg-no-repeat bg-contain',
-                //   s[getFileType(getSourceName(indexingStatusDetail.id))] || s.unknownFileIcon,
-                // )} />
-                <DocumentFileIcon
-                  className="shrink-0 size-4"
-                  name={getSourceName(indexingStatusDetail.id)}
-                  extension={getFileType(getSourceName(indexingStatusDetail.id))}
-                />
+                <div className={cn(s.fileIcon, s[getFileType(getSourceName(indexingStatusDetail.id))])} />
               )}
               {getSourceType(indexingStatusDetail.id) === DataSourceType.NOTION && (
                 <NotionIcon
-                  className='shrink-0'
+                  className='shrink-0 mr-1'
                   type='page'
                   src={getIcon(indexingStatusDetail.id)}
                 />
               )}
-              <div className="grow flex items-center gap-1 w-0" title={getSourceName(indexingStatusDetail.id)}>
-                <div className="text-xs truncate">
-                  {getSourceName(indexingStatusDetail.id)}
-                </div>
-                {
-                  enableBilling && (
-                    <PriorityLabel className='ml-0' />
-                  )
-                }
-              </div>
+              <div className={`${s.name} truncate`} title={getSourceName(indexingStatusDetail.id)}>{getSourceName(indexingStatusDetail.id)}</div>
+              {
+                enableBilling && (
+                  <PriorityLabel />
+                )
+              }
+            </div>
+            <div className='shrink-0'>
               {isSourceEmbedding(indexingStatusDetail) && (
-                <div className="shrink-0 text-xs">{`${getSourcePercent(indexingStatusDetail)}%`}</div>
+                <div className={s.percent}>{`${getSourcePercent(indexingStatusDetail)}%`}</div>
               )}
-              {indexingStatusDetail.indexing_status === 'error' && (
+              {indexingStatusDetail.indexing_status === 'error' && indexingStatusDetail.error && (
                 <Tooltip
-                  popupClassName='px-4 py-[14px] max-w-60 text-sm leading-4 text-text-secondary border-[0.5px] border-components-panel-border rounded-xl'
-                  offset={4}
-                  popupContent={indexingStatusDetail.error}
+                  popupContent={(
+                    <div className='max-w-[400px]'>
+                      {indexingStatusDetail.error}
+                    </div>
+                  )}
                 >
-                  <span>
-                    <RiErrorWarningFill className='shrink-0 size-4 text-text-destructive' />
-                  </span>
+                  <div className={cn(s.percent, s.error, 'flex items-center')}>
+                    Error
+                    <RiErrorWarningFill className='ml-1 w-4 h-4' />
+                  </div>
                 </Tooltip>
               )}
+              {indexingStatusDetail.indexing_status === 'error' && !indexingStatusDetail.error && (
+                <div className={cn(s.percent, s.error, 'flex items-center')}>
+                  Error
+                </div>
+              )}
               {indexingStatusDetail.indexing_status === 'completed' && (
-                <RiCheckboxCircleFill className='shrink-0 size-4 text-text-success' />
+                <div className={cn(s.percent, s.success)}>100%</div>
               )}
             </div>
           </div>
         ))}
       </div>
-      <hr className="my-3 h-[1px] bg-divider-subtle border-0" />
-      <RuleDetail
-        sourceData={ruleDetail}
-        indexingType={indexingType}
-        retrievalMethod={retrievalMethod}
-      />
-      <div className='flex items-center gap-2 my-10'>
-        <Button className='w-fit' onClick={navToApiDocs}>
-          <RiTerminalBoxLine className='size-4 mr-2' />
-          <span>Access the API</span>
-        </Button>
+      <RuleDetail sourceData={ruleDetail} />
+      <div className='flex items-center gap-2 mt-10'>
         <Button className='w-fit' variant='primary' onClick={navToDocumentList}>
           <span>{t('datasetCreation.stepThree.navTo')}</span>
-          <ArrowRightIcon className='size-4 ml-2 stroke-current stroke-1' />
+          <ArrowRightIcon className='h-4 w-4 ml-2 stroke-current stroke-1' />
         </Button>
       </div>
     </>

+ 1 - 2
web/app/components/datasets/create/file-preview/index.module.css

@@ -1,6 +1,6 @@
 .filePreview {
     @apply flex flex-col border-l border-gray-200 shrink-0;
-    width: 100%;
+    width: 528px;
     background-color: #fcfcfd;
   }
   
@@ -48,6 +48,5 @@
   }
   .fileContent {
     white-space: pre-line;
-    word-break: break-all;
   }
   

+ 2 - 2
web/app/components/datasets/create/file-preview/index.tsx

@@ -44,7 +44,7 @@ const FilePreview = ({
   }, [file])
 
   return (
-    <div className={cn(s.filePreview, 'h-full')}>
+    <div className={cn(s.filePreview)}>
       <div className={cn(s.previewHeader)}>
         <div className={cn(s.title)}>
           <span>{t('datasetCreation.stepOne.filePreview')}</span>
@@ -59,7 +59,7 @@ const FilePreview = ({
       <div className={cn(s.previewContent)}>
         {loading && <div className={cn(s.loading)} />}
         {!loading && (
-          <div className={cn(s.fileContent, 'body-md-regular')}>{previewContent}</div>
+          <div className={cn(s.fileContent)}>{previewContent}</div>
         )}
       </div>
     </div>

+ 66 - 1
web/app/components/datasets/create/file-uploader/index.module.css

@@ -1,3 +1,68 @@
+.fileUploader {
+  @apply mb-6;
+}
+
+.fileUploader .title {
+  @apply mb-2;
+  font-weight: 500;
+  font-size: 16px;
+  line-height: 24px;
+  color: #344054;
+}
+
+.fileUploader .tip {
+  font-weight: 400;
+  font-size: 12px;
+  line-height: 18px;
+  color: #667085;
+}
+
+.uploader {
+  @apply relative box-border flex justify-center items-center mb-2 p-3;
+  flex-direction: column;
+  max-width: 640px;
+  min-height: 80px;
+  background: #F9FAFB;
+  border: 1px dashed #EAECF0;
+  border-radius: 12px;
+  font-weight: 400;
+  font-size: 14px;
+  line-height: 20px;
+  color: #667085;
+}
+
+.uploader.dragging {
+  background: #F5F8FF;
+  border: 1px dashed #B2CCFF;
+}
+
+.uploader .draggingCover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.uploader .uploadIcon {
+  content: '';
+  display: block;
+  margin-right: 8px;
+  width: 24px;
+  height: 24px;
+  background: center no-repeat url(../assets/upload-cloud-01.svg);
+  background-size: contain;
+}
+
+.uploader .browse {
+  @apply pl-1 cursor-pointer;
+  color: #155eef;
+}
+
+.fileList {
+  @apply space-y-2;
+}
+
 .file {
   @apply box-border relative flex items-center justify-between;
   padding: 8px 12px 8px 8px;
@@ -128,4 +193,4 @@
 
 .file:hover .actionWrapper .remove {
   display: block;
-}
+}

+ 28 - 54
web/app/components/datasets/create/file-uploader/index.tsx

@@ -3,12 +3,10 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
 import useSWR from 'swr'
-import { RiDeleteBinLine, RiUploadCloud2Line } from '@remixicon/react'
-import DocumentFileIcon from '../../common/document-file-icon'
+import s from './index.module.css'
 import cn from '@/utils/classnames'
 import type { CustomFile as File, FileItem } from '@/models/datasets'
 import { ToastContext } from '@/app/components/base/toast'
-import SimplePieChart from '@/app/components/base/simple-pie-chart'
 
 import { upload } from '@/service/base'
 import { fetchFileUploadConfig } from '@/service/common'
@@ -16,8 +14,6 @@ import { fetchSupportFileTypes } from '@/service/datasets'
 import I18n from '@/context/i18n'
 import { LanguagesSupported } from '@/i18n/language'
 import { IS_CE_EDITION } from '@/config'
-import { useAppContext } from '@/context/app-context'
-import { Theme } from '@/types/app'
 
 const FILES_NUMBER_LIMIT = 20
 
@@ -226,9 +222,6 @@ const FileUploader = ({
     initialUpload(files.filter(isValid))
   }, [isValid, initialUpload])
 
-  const { theme } = useAppContext()
-  const chartColor = useMemo(() => theme === Theme.dark ? '#5289ff' : '#296dff', [theme])
-
   useEffect(() => {
     dropRef.current?.addEventListener('dragenter', handleDragEnter)
     dropRef.current?.addEventListener('dragover', handleDragOver)
@@ -243,12 +236,12 @@ const FileUploader = ({
   }, [handleDrop])
 
   return (
-    <div className="mb-5 w-[640px]">
+    <div className={s.fileUploader}>
       {!hideUpload && (
         <input
           ref={fileUploader}
           id="fileUploader"
-          className="hidden"
+          style={{ display: 'none' }}
           type="file"
           multiple={!notSupportBatchUpload}
           accept={ACCEPTS.join(',')}
@@ -256,71 +249,52 @@ const FileUploader = ({
         />
       )}
 
-      <div className={cn('text-text-tertiary text-sm font-semibold leading-6 mb-1', titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
-
+      <div className={cn(s.title, titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
       {!hideUpload && (
-        <div ref={dropRef} className={cn('relative box-border flex flex-col justify-center items-center gap-1 mb-2 px-4 py-3 max-w-[640px] min-h-20 leading-4 text-xs text-text-tertiary bg-components-dropzone-bg border border-dashed border-components-dropzone-border rounded-xl', dragging && 'bg-components-dropzone-bg-accent border-components-dropzone-border-accent')}>
-          <div className="flex justify-center items-center min-h-5 text-sm leading-4 text-text-secondary">
-            <RiUploadCloud2Line className='mr-2 size-5' />
 
+        <div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
+          <div className='flex justify-center items-center min-h-6 mb-2'>
+            <span className={s.uploadIcon} />
             <span>
               {t('datasetCreation.stepOne.uploader.button')}
-              {supportTypes.length > 0 && (
-                <label className="ml-1 text-text-accent cursor-pointer" onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
-              )}
+              <label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
             </span>
           </div>
-          <div>{t('datasetCreation.stepOne.uploader.tip', {
+          <div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip', {
             size: fileUploadConfig.file_size_limit,
             supportTypes: supportTypesShowNames,
           })}</div>
-          {dragging && <div ref={dragRef} className='absolute top-0 left-0 w-full h-full' />}
+          {dragging && <div ref={dragRef} className={s.draggingCover} />}
         </div>
       )}
-      <div className='space-y-1 max-w-[640px] cursor-default'>
-
+      <div className={s.fileList}>
         {fileList.map((fileItem, index) => (
           <div
             key={`${fileItem.fileID}-${index}`}
             onClick={() => fileItem.file?.id && onPreview(fileItem.file)}
             className={cn(
-              'flex items-center h-12 max-w-[640px] bg-components-panel-on-panel-item-bg text-xs leading-3 text-text-tertiary border border-components-panel-border rounded-lg shadow-xs',
-              // 'border-state-destructive-border bg-state-destructive-hover',
+              s.file,
+              fileItem.progress < 100 && s.uploading,
             )}
           >
-            <div className="shrink-0 flex justify-center items-center w-12">
-              <DocumentFileIcon
-                className="shrink-0 size-6"
-                name={fileItem.file.name}
-                extension={getFileType(fileItem.file)}
-              />
-            </div>
-            <div className="grow shrink flex flex-col gap-0.5">
-              <div className='flex w-full'>
-                <div className="text-sm leading-4 text-text-secondary w-0 grow truncate">{fileItem.file.name}</div>
-              </div>
-              <div className="w-full leading-3 truncate text-text-tertiary">
-                <span className='uppercase'>{getFileType(fileItem.file)}</span>
-                <span className='px-1 text-text-quaternary'>·</span>
-                <span>{getFileSize(fileItem.file.size)}</span>
-                {/* <span className='px-1 text-text-quaternary'>·</span>
-                  <span>10k characters</span> */}
-              </div>
+            {fileItem.progress < 100 && (
+              <div className={s.progressbar} style={{ width: `${fileItem.progress}%` }} />
+            )}
+            <div className={s.fileInfo}>
+              <div className={cn(s.fileIcon, s[getFileType(fileItem.file)])} />
+              <div className={s.filename}>{fileItem.file.name}</div>
+              <div className={s.size}>{getFileSize(fileItem.file.size)}</div>
             </div>
-            <div className="shrink-0 flex items-center justify-end gap-1 pr-3 w-16">
-              {/* <span className="flex justify-center items-center w-6 h-6 cursor-pointer">
-                  <RiErrorWarningFill className='size-4 text-text-warning' />
-                </span> */}
+            <div className={s.actionWrapper}>
               {(fileItem.progress < 100 && fileItem.progress >= 0) && (
-                // <div className={s.percent}>{`${fileItem.progress}%`}</div>
-                <SimplePieChart percentage={fileItem.progress} stroke={chartColor} fill={chartColor} animationDuration={0} />
+                <div className={s.percent}>{`${fileItem.progress}%`}</div>
+              )}
+              {fileItem.progress === 100 && (
+                <div className={s.remove} onClick={(e) => {
+                  e.stopPropagation()
+                  removeFile(fileItem.fileID)
+                }} />
               )}
-              <span className="flex justify-center items-center w-6 h-6 cursor-pointer" onClick={(e) => {
-                e.stopPropagation()
-                removeFile(fileItem.fileID)
-              }}>
-                <RiDeleteBinLine className='size-4 text-text-tertiary' />
-              </span>
             </div>
           </div>
         ))}

+ 0 - 16
web/app/components/datasets/create/icons.ts

@@ -1,16 +0,0 @@
-import GoldIcon from './assets/gold.svg'
-import Piggybank from './assets/piggy-bank-mod.svg'
-import Selection from './assets/selection-mod.svg'
-import Research from './assets/research-mod.svg'
-import PatternRecognition from './assets/pattern-recognition-mod.svg'
-
-export const indexMethodIcon = {
-  high_quality: GoldIcon,
-  economical: Piggybank,
-}
-
-export const retrievalIcon = {
-  vector: Selection,
-  fullText: Research,
-  hybrid: PatternRecognition,
-}

+ 28 - 30
web/app/components/datasets/create/index.tsx

@@ -3,10 +3,10 @@ import React, { useCallback, useEffect, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import AppUnavailable from '../../base/app-unavailable'
 import { ModelTypeEnum } from '../../header/account-setting/model-provider-page/declarations'
+import StepsNavBar from './steps-nav-bar'
 import StepOne from './step-one'
 import StepTwo from './step-two'
 import StepThree from './step-three'
-import { Topbar } from './top-bar'
 import { DataSourceType } from '@/models/datasets'
 import type { CrawlOptions, CrawlResultItem, DataSet, FileItem, createDocumentResponse } from '@/models/datasets'
 import { fetchDataSource } from '@/service/common'
@@ -36,7 +36,6 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
   const [dataSourceType, setDataSourceType] = useState<DataSourceType>(DataSourceType.FILE)
   const [step, setStep] = useState(1)
   const [indexingTypeCache, setIndexTypeCache] = useState('')
-  const [retrievalMethodCache, setRetrievalMethodCache] = useState('')
   const [fileList, setFiles] = useState<FileItem[]>([])
   const [result, setResult] = useState<createDocumentResponse | undefined>()
   const [hasError, setHasError] = useState(false)
@@ -81,9 +80,6 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
   const updateResultCache = (res?: createDocumentResponse) => {
     setResult(res)
   }
-  const updateRetrievalMethodCache = (method: string) => {
-    setRetrievalMethodCache(method)
-  }
 
   const nextStep = useCallback(() => {
     setStep(step + 1)
@@ -122,29 +118,33 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
     return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
 
   return (
-    <div className='flex flex-col bg-components-panel-bg' style={{ height: 'calc(100vh - 56px)' }}>
-      <Topbar activeIndex={step - 1} />
-      <div style={{ height: 'calc(100% - 52px)' }}>
-        {step === 1 && <StepOne
-          hasConnection={hasConnection}
-          onSetting={() => setShowAccountSettingModal({ payload: 'data-source' })}
-          datasetId={datasetId}
-          dataSourceType={dataSourceType}
-          dataSourceTypeDisable={!!detail?.data_source_type}
-          changeType={setDataSourceType}
-          files={fileList}
-          updateFile={updateFile}
-          updateFileList={updateFileList}
-          notionPages={notionPages}
-          updateNotionPages={updateNotionPages}
-          onStepChange={nextStep}
-          websitePages={websitePages}
-          updateWebsitePages={setWebsitePages}
-          onWebsiteCrawlProviderChange={setWebsiteCrawlProvider}
-          onWebsiteCrawlJobIdChange={setWebsiteCrawlJobId}
-          crawlOptions={crawlOptions}
-          onCrawlOptionsChange={setCrawlOptions}
-        />}
+    <div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
+      <div className="flex flex-col w-11 sm:w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
+        <StepsNavBar step={step} datasetId={datasetId} />
+      </div>
+      <div className="grow bg-white">
+        <div className={step === 1 ? 'block h-full' : 'hidden'}>
+          <StepOne
+            hasConnection={hasConnection}
+            onSetting={() => setShowAccountSettingModal({ payload: 'data-source' })}
+            datasetId={datasetId}
+            dataSourceType={dataSourceType}
+            dataSourceTypeDisable={!!detail?.data_source_type}
+            changeType={setDataSourceType}
+            files={fileList}
+            updateFile={updateFile}
+            updateFileList={updateFileList}
+            notionPages={notionPages}
+            updateNotionPages={updateNotionPages}
+            onStepChange={nextStep}
+            websitePages={websitePages}
+            updateWebsitePages={setWebsitePages}
+            onWebsiteCrawlProviderChange={setWebsiteCrawlProvider}
+            onWebsiteCrawlJobIdChange={setWebsiteCrawlJobId}
+            crawlOptions={crawlOptions}
+            onCrawlOptionsChange={setCrawlOptions}
+          />
+        </div>
         {(step === 2 && (!datasetId || (datasetId && !!detail))) && <StepTwo
           isAPIKeySet={!!embeddingsDefaultModel}
           onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
@@ -158,7 +158,6 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
           websiteCrawlJobId={websiteCrawlJobId}
           onStepChange={changeStep}
           updateIndexingTypeCache={updateIndexingTypeCache}
-          updateRetrievalMethodCache={updateRetrievalMethodCache}
           updateResultCache={updateResultCache}
           crawlOptions={crawlOptions}
         />}
@@ -166,7 +165,6 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
           datasetId={datasetId}
           datasetName={detail?.name}
           indexingType={detail?.indexing_technique || indexingTypeCache}
-          retrievalMethod={detail?.retrieval_model_dict?.search_method || retrievalMethodCache}
           creationCache={result}
         />}
       </div>

+ 2 - 2
web/app/components/datasets/create/notion-page-preview/index.tsx

@@ -44,7 +44,7 @@ const NotionPagePreview = ({
   }, [currentPage])
 
   return (
-    <div className={cn(s.filePreview, 'h-full')}>
+    <div className={cn(s.filePreview)}>
       <div className={cn(s.previewHeader)}>
         <div className={cn(s.title)}>
           <span>{t('datasetCreation.stepOne.pagePreview')}</span>
@@ -64,7 +64,7 @@ const NotionPagePreview = ({
       <div className={cn(s.previewContent)}>
         {loading && <div className={cn(s.loading)} />}
         {!loading && (
-          <div className={cn(s.fileContent, 'body-md-regular')}>{previewContent}</div>
+          <div className={cn(s.fileContent)}>{previewContent}</div>
         )}
       </div>
     </div>

+ 23 - 15
web/app/components/datasets/create/step-one/index.module.css

@@ -2,19 +2,21 @@
   position: sticky;
   top: 0;
   left: 0;
-  padding: 42px 64px 12px 0;
+  padding: 42px 64px 12px;
   font-weight: 600;
   font-size: 18px;
   line-height: 28px;
+  color: #101828;
 }
 
 .form {
   position: relative;
   padding: 12px 64px;
+  background-color: #fff;
 }
 
 .dataSourceItem {
-  @apply box-border relative grow shrink-0 flex items-center p-3 h-14 bg-white rounded-xl cursor-pointer;
+  @apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
   border: 0.5px solid #EAECF0;
   box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
   font-weight: 500;
@@ -22,32 +24,27 @@
   line-height: 20px;
   color: #101828;
 }
-
 .dataSourceItem:hover {
   background-color: #f5f8ff;
   border: 0.5px solid #B2CCFF;
   box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
 }
-
 .dataSourceItem.active {
   background-color: #f5f8ff;
   border: 1.5px solid #528BFF;
   box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
 }
-
 .dataSourceItem.disabled {
   background-color: #f9fafb;
   border: 0.5px solid #EAECF0;
   box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
   cursor: default;
 }
-
 .dataSourceItem.disabled:hover {
   background-color: #f9fafb;
   border: 0.5px solid #EAECF0;
   box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
 }
-
 .comingTag {
   @apply flex justify-center items-center bg-white;
   position: absolute;
@@ -62,7 +59,6 @@
   line-height: 18px;
   color: #444CE7;
 }
-
 .datasetIcon {
   @apply flex mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat;
   background-color: #F5FAFF;
@@ -70,18 +66,15 @@
   background-size: 16px;
   border: 0.5px solid #D1E9FF;
 }
-
 .dataSourceItem:active .datasetIcon,
 .dataSourceItem:hover .datasetIcon {
   background-color: #F5F8FF;
   border: 0.5px solid #E0EAFF;
 }
-
 .datasetIcon.notion {
   background-image: url(../assets/notion.svg);
   background-size: 20px;
 }
-
 .datasetIcon.web {
   background-image: url(../assets/web.svg);
 }
@@ -97,12 +90,29 @@
   background-color: #eaecf0;
 }
 
+.OtherCreationOption {
+  @apply flex items-center cursor-pointer;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 18px;
+  color: #155EEF;
+}
+.OtherCreationOption::before {
+  content: '';
+  display: block;
+  margin-right: 4px;
+  width: 16px;
+  height: 16px;
+  background: center no-repeat url(../assets/folder-plus.svg);
+  background-size: contain;
+}
+
 .notionConnectionTip {
   display: flex;
   flex-direction: column;
   align-items: flex-start;
   padding: 24px;
-  width: 640px;
+  max-width: 640px;
   background: #F9FAFB;
   border-radius: 16px;
 }
@@ -128,7 +138,6 @@
   line-height: 24px;
   color: #374151;
 }
-
 .notionConnectionTip .title::after {
   content: '';
   position: absolute;
@@ -139,7 +148,6 @@
   background: center no-repeat url(../assets/Icon-3-dots.svg);
   background-size: contain;
 }
-
 .notionConnectionTip .tip {
   margin-bottom: 20px;
   font-style: normal;
@@ -147,4 +155,4 @@
   font-size: 13px;
   line-height: 18px;
   color: #6B7280;
-}
+}

+ 127 - 160
web/app/components/datasets/create/step-one/index.tsx

@@ -1,7 +1,6 @@
 'use client'
 import React, { useMemo, useState } from 'react'
 import { useTranslation } from 'react-i18next'
-import { RiArrowRightLine, RiFolder6Line } from '@remixicon/react'
 import FilePreview from '../file-preview'
 import FileUploader from '../file-uploader'
 import NotionPagePreview from '../notion-page-preview'
@@ -18,7 +17,6 @@ import { NotionPageSelector } from '@/app/components/base/notion-page-selector'
 import { useDatasetDetailContext } from '@/context/dataset-detail'
 import { useProviderContext } from '@/context/provider-context'
 import VectorSpaceFull from '@/app/components/billing/vector-space-full'
-import classNames from '@/utils/classnames'
 
 type IStepOneProps = {
   datasetId?: string
@@ -122,174 +120,143 @@ const StepOne = ({
       return true
     if (isShowVectorSpaceFull)
       return true
-    return false
-  }, [files, isShowVectorSpaceFull])
 
+    return false
+  }, [files])
   return (
     <div className='flex w-full h-full'>
-      <div className='w-1/2 h-full overflow-y-auto relative'>
-        <div className='flex justify-end'>
-          <div className={classNames(s.form)}>
-            {
-              shouldShowDataSourceTypeList && (
-                <div className={classNames(s.stepHeader, 'z-10 text-text-secondary bg-components-panel-bg-blur')}>{t('datasetCreation.steps.one')}</div>
-              )
-            }
-            {
-              shouldShowDataSourceTypeList && (
-                <div className='flex items-center mb-8 flex-wrap gap-4'>
-                  <div
-                    className={cn(
-                      s.dataSourceItem,
-                      dataSourceType === DataSourceType.FILE && s.active,
-                      dataSourceTypeDisable && dataSourceType !== DataSourceType.FILE && s.disabled,
-                    )}
-                    onClick={() => {
-                      if (dataSourceTypeDisable)
-                        return
-                      changeType(DataSourceType.FILE)
-                      hideFilePreview()
-                      hideNotionPagePreview()
-                    }}
-                  >
-                    <span className={cn(s.datasetIcon)} />
-                    {t('datasetCreation.stepOne.dataSourceType.file')}
-                  </div>
-                  <div
-                    className={cn(
-                      s.dataSourceItem,
-                      dataSourceType === DataSourceType.NOTION && s.active,
-                      dataSourceTypeDisable && dataSourceType !== DataSourceType.NOTION && s.disabled,
-                    )}
-                    onClick={() => {
-                      if (dataSourceTypeDisable)
-                        return
-                      changeType(DataSourceType.NOTION)
-                      hideFilePreview()
-                      hideNotionPagePreview()
-                    }}
-                  >
-                    <span className={cn(s.datasetIcon, s.notion)} />
-                    {t('datasetCreation.stepOne.dataSourceType.notion')}
-                  </div>
-                  <div
-                    className={cn(
-                      s.dataSourceItem,
-                      dataSourceType === DataSourceType.WEB && s.active,
-                      dataSourceTypeDisable && dataSourceType !== DataSourceType.WEB && s.disabled,
-                    )}
-                    onClick={() => changeType(DataSourceType.WEB)}
-                  >
-                    <span className={cn(s.datasetIcon, s.web)} />
-                    {t('datasetCreation.stepOne.dataSourceType.web')}
-                  </div>
+      <div className='grow overflow-y-auto relative'>
+        {
+          shouldShowDataSourceTypeList && (
+            <div className={s.stepHeader}>{t('datasetCreation.steps.one')}</div>
+          )
+        }
+        <div className={s.form}>
+          {
+            shouldShowDataSourceTypeList && (
+              <div className='flex items-center mb-8 flex-wrap gap-y-4'>
+                <div
+                  className={cn(
+                    s.dataSourceItem,
+                    dataSourceType === DataSourceType.FILE && s.active,
+                    dataSourceTypeDisable && dataSourceType !== DataSourceType.FILE && s.disabled,
+                  )}
+                  onClick={() => {
+                    if (dataSourceTypeDisable)
+                      return
+                    changeType(DataSourceType.FILE)
+                    hideFilePreview()
+                    hideNotionPagePreview()
+                  }}
+                >
+                  <span className={cn(s.datasetIcon)} />
+                  {t('datasetCreation.stepOne.dataSourceType.file')}
                 </div>
-              )
-            }
-            {dataSourceType === DataSourceType.FILE && (
-              <>
-                <FileUploader
-                  fileList={files}
-                  titleClassName={!shouldShowDataSourceTypeList ? 'mt-[30px] !mb-[44px] !text-lg !font-semibold !text-gray-900' : undefined}
-                  prepareFileList={updateFileList}
-                  onFileListUpdate={updateFileList}
-                  onFileUpdate={updateFile}
-                  onPreview={updateCurrentFile}
-                  notSupportBatchUpload={notSupportBatchUpload}
-                />
-                {isShowVectorSpaceFull && (
-                  <div className='max-w-[640px] mb-4'>
-                    <VectorSpaceFull />
-                  </div>
-                )}
-                <div className="flex justify-end gap-2 max-w-[640px]">
-                  {/* <Button>{t('datasetCreation.stepOne.cancel')}</Button> */}
-                  <Button disabled={nextDisabled} variant='primary' onClick={onStepChange}>
-                    <span className="flex gap-0.5 px-[10px]">
-                      <span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
-                      <RiArrowRightLine className="size-4" />
-                    </span>
-                  </Button>
+                <div
+                  className={cn(
+                    s.dataSourceItem,
+                    dataSourceType === DataSourceType.NOTION && s.active,
+                    dataSourceTypeDisable && dataSourceType !== DataSourceType.NOTION && s.disabled,
+                  )}
+                  onClick={() => {
+                    if (dataSourceTypeDisable)
+                      return
+                    changeType(DataSourceType.NOTION)
+                    hideFilePreview()
+                    hideNotionPagePreview()
+                  }}
+                >
+                  <span className={cn(s.datasetIcon, s.notion)} />
+                  {t('datasetCreation.stepOne.dataSourceType.notion')}
                 </div>
-              </>
-            )}
-            {dataSourceType === DataSourceType.NOTION && (
-              <>
-                {!hasConnection && <NotionConnector onSetting={onSetting} />}
-                {hasConnection && (
-                  <>
-                    <div className='mb-8 w-[640px]'>
-                      <NotionPageSelector
-                        value={notionPages.map(page => page.page_id)}
-                        onSelect={updateNotionPages}
-                        onPreview={updateCurrentPage}
-                      />
-                    </div>
-                    {isShowVectorSpaceFull && (
-                      <div className='max-w-[640px] mb-4'>
-                        <VectorSpaceFull />
-                      </div>
-                    )}
-                    <div className="flex justify-end gap-2 max-w-[640px]">
-                      {/* <Button>{t('datasetCreation.stepOne.cancel')}</Button> */}
-                      <Button disabled={isShowVectorSpaceFull || !notionPages.length} variant='primary' onClick={onStepChange}>
-                        <span className="flex gap-0.5 px-[10px]">
-                          <span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
-                          <RiArrowRightLine className="size-4" />
-                        </span>
-                      </Button>
-                    </div>
-                  </>
-                )}
-              </>
-            )}
-            {dataSourceType === DataSourceType.WEB && (
-              <>
-                <div className={cn('mb-8 w-[640px]', !shouldShowDataSourceTypeList && 'mt-12')}>
-                  <Website
-                    onPreview={setCurrentWebsite}
-                    checkedCrawlResult={websitePages}
-                    onCheckedCrawlResultChange={updateWebsitePages}
-                    onCrawlProviderChange={onWebsiteCrawlProviderChange}
-                    onJobIdChange={onWebsiteCrawlJobIdChange}
-                    crawlOptions={crawlOptions}
-                    onCrawlOptionsChange={onCrawlOptionsChange}
-                  />
+                <div
+                  className={cn(
+                    s.dataSourceItem,
+                    dataSourceType === DataSourceType.WEB && s.active,
+                    dataSourceTypeDisable && dataSourceType !== DataSourceType.WEB && s.disabled,
+                  )}
+                  onClick={() => changeType(DataSourceType.WEB)}
+                >
+                  <span className={cn(s.datasetIcon, s.web)} />
+                  {t('datasetCreation.stepOne.dataSourceType.web')}
+                </div>
+              </div>
+            )
+          }
+          {dataSourceType === DataSourceType.FILE && (
+            <>
+              <FileUploader
+                fileList={files}
+                titleClassName={!shouldShowDataSourceTypeList ? 'mt-[30px] !mb-[44px] !text-lg !font-semibold !text-gray-900' : undefined}
+                prepareFileList={updateFileList}
+                onFileListUpdate={updateFileList}
+                onFileUpdate={updateFile}
+                onPreview={updateCurrentFile}
+                notSupportBatchUpload={notSupportBatchUpload}
+              />
+              {isShowVectorSpaceFull && (
+                <div className='max-w-[640px] mb-4'>
+                  <VectorSpaceFull />
                 </div>
-                {isShowVectorSpaceFull && (
-                  <div className='max-w-[640px] mb-4'>
-                    <VectorSpaceFull />
+              )}
+              <Button disabled={nextDisabled} className={s.submitButton} variant='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
+            </>
+          )}
+          {dataSourceType === DataSourceType.NOTION && (
+            <>
+              {!hasConnection && <NotionConnector onSetting={onSetting} />}
+              {hasConnection && (
+                <>
+                  <div className='mb-8 w-[640px]'>
+                    <NotionPageSelector
+                      value={notionPages.map(page => page.page_id)}
+                      onSelect={updateNotionPages}
+                      onPreview={updateCurrentPage}
+                    />
                   </div>
-                )}
-                <div className="flex justify-end gap-2 max-w-[640px]">
-                  {/* <Button>{t('datasetCreation.stepOne.cancel')}</Button> */}
-                  <Button disabled={isShowVectorSpaceFull || !websitePages.length} variant='primary' onClick={onStepChange}>
-                    <span className="flex gap-0.5 px-[10px]">
-                      <span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
-                      <RiArrowRightLine className="size-4" />
-                    </span>
-                  </Button>
+                  {isShowVectorSpaceFull && (
+                    <div className='max-w-[640px] mb-4'>
+                      <VectorSpaceFull />
+                    </div>
+                  )}
+                  <Button disabled={isShowVectorSpaceFull || !notionPages.length} className={s.submitButton} variant='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
+                </>
+              )}
+            </>
+          )}
+          {dataSourceType === DataSourceType.WEB && (
+            <>
+              <div className={cn('mb-8 w-[640px]', !shouldShowDataSourceTypeList && 'mt-12')}>
+                <Website
+                  onPreview={setCurrentWebsite}
+                  checkedCrawlResult={websitePages}
+                  onCheckedCrawlResultChange={updateWebsitePages}
+                  onCrawlProviderChange={onWebsiteCrawlProviderChange}
+                  onJobIdChange={onWebsiteCrawlJobIdChange}
+                  crawlOptions={crawlOptions}
+                  onCrawlOptionsChange={onCrawlOptionsChange}
+                />
+              </div>
+              {isShowVectorSpaceFull && (
+                <div className='max-w-[640px] mb-4'>
+                  <VectorSpaceFull />
                 </div>
-              </>
-            )}
-            {!datasetId && (
-              <>
-                <div className={s.dividerLine} />
-                <span className="inline-flex items-center cursor-pointer text-[13px] leading-4 text-text-accent" onClick={modalShowHandle}>
-                  <RiFolder6Line className="size-4 mr-1" />
-                  {t('datasetCreation.stepOne.emptyDatasetCreation')}
-                </span>
-              </>
-            )}
-          </div>
-          <EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle} />
+              )}
+              <Button disabled={isShowVectorSpaceFull || !websitePages.length} className={s.submitButton} variant='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
+            </>
+          )}
+          {!datasetId && (
+            <>
+              <div className={s.dividerLine} />
+              <div onClick={modalShowHandle} className={s.OtherCreationOption}>{t('datasetCreation.stepOne.emptyDatasetCreation')}</div>
+            </>
+          )}
         </div>
+        <EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle} />
       </div>
-      <div className='w-1/2 h-full overflow-y-auto'>
-        {currentFile && <FilePreview file={currentFile} hidePreview={hideFilePreview} />}
-        {currentNotionPage && <NotionPagePreview currentPage={currentNotionPage} hidePreview={hideNotionPagePreview} />}
-        {currentWebsite && <WebsitePreview payload={currentWebsite} hidePreview={hideWebsitePreview} />}
-      </div>
+      {currentFile && <FilePreview file={currentFile} hidePreview={hideFilePreview} />}
+      {currentNotionPage && <NotionPagePreview currentPage={currentNotionPage} hidePreview={hideNotionPagePreview} />}
+      {currentWebsite && <WebsitePreview payload={currentWebsite} hidePreview={hideWebsitePreview} />}
     </div>
   )
 }

+ 21 - 32
web/app/components/datasets/create/step-three/index.tsx

@@ -1,51 +1,45 @@
 'use client'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import { RiBookOpenLine } from '@remixicon/react'
 import EmbeddingProcess from '../embedding-process'
 
+import s from './index.module.css'
+import cn from '@/utils/classnames'
 import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
-import AppIcon from '@/app/components/base/app-icon'
 
 type StepThreeProps = {
   datasetId?: string
   datasetName?: string
   indexingType?: string
-  retrievalMethod?: string
   creationCache?: createDocumentResponse
 }
 
-const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrievalMethod }: StepThreeProps) => {
+const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
   const { t } = useTranslation()
 
   const media = useBreakpoints()
   const isMobile = media === MediaType.mobile
 
   return (
-    <div className="flex justify-center w-full max-h-full h-full overflow-y-auto">
-      <div className="grow shrink-0 h-full max-w-[960px] overflow-y-auto px-14 sm:px-16">
-        <div className="mx-auto max-w-[640px]">
+    <div className='flex w-full h-full'>
+      <div className={'h-full w-full overflow-y-scroll px-6 sm:px-16'}>
+        <div className='max-w-[636px]'>
           {!datasetId && (
             <>
-              <div className="pt-10">
-                <div className="mb-1 text-xl leading-[22px] font-semibold text-text-primary">{t('datasetCreation.stepThree.creationTitle')}</div>
-                <div className="mb-7 text-[13px] leading-4 text-text-tertiary">{t('datasetCreation.stepThree.creationContent')}</div>
-                <div className="flex gap-4">
-                  <AppIcon {...creationCache?.dataset} className="size-14 text-2xl self-center" />
-                  <div className="grow flex flex-col gap-1">
-                    <div className="text-[13px] leading-6 font-semibold">{t('datasetCreation.stepThree.label')}</div>
-                    <div className="w-full px-3 py-2 text-[13px] leading-4 bg-components-input-bg-normal rounded-lg truncate">{datasetName || creationCache?.dataset?.name}</div>
-                  </div>
-                </div>
+              <div className={s.creationInfo}>
+                <div className={s.title}>{t('datasetCreation.stepThree.creationTitle')}</div>
+                <div className={s.content}>{t('datasetCreation.stepThree.creationContent')}</div>
+                <div className={s.label}>{t('datasetCreation.stepThree.label')}</div>
+                <div className={s.datasetName}>{datasetName || creationCache?.dataset?.name}</div>
               </div>
-              <hr className="my-6 h-[1px] bg-divider-subtle border-0" />
+              <div className={s.dividerLine} />
             </>
           )}
           {datasetId && (
-            <div className="pt-10">
-              <div className="mb-1 text-xl leading-[22px] font-semibold text-text-primary">{t('datasetCreation.stepThree.additionTitle')}</div>
-              <div className="mb-7 text-[13px] leading-4 text-text-tertiary">{`${t('datasetCreation.stepThree.additionP1')} ${datasetName || creationCache?.dataset?.name} ${t('datasetCreation.stepThree.additionP2')}`}</div>
+            <div className={s.creationInfo}>
+              <div className={s.title}>{t('datasetCreation.stepThree.additionTitle')}</div>
+              <div className={s.content}>{`${t('datasetCreation.stepThree.additionP1')} ${datasetName || creationCache?.dataset?.name} ${t('datasetCreation.stepThree.additionP2')}`}</div>
             </div>
           )}
           <EmbeddingProcess
@@ -53,21 +47,16 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
             batchId={creationCache?.batch || ''}
             documents={creationCache?.documents as FullDocumentDetail[]}
             indexingType={indexingType || creationCache?.dataset?.indexing_technique}
-            retrievalMethod={retrievalMethod || creationCache?.dataset?.retrieval_model?.search_method}
           />
         </div>
       </div>
-      {!isMobile && (
-        <div className="shrink-0 pt-[88px] pr-8 text-xs">
-          <div className="flex flex-col gap-3 w-[328px] p-6 text-text-tertiary bg-background-section rounded-xl">
-            <div className="flex justify-center items-center size-10 bg-components-card-bg rounded-[10px] shadow-lg">
-              <RiBookOpenLine className="size-5 text-text-accent" />
-            </div>
-            <div className="text-base font-semibold text-text-secondary">{t('datasetCreation.stepThree.sideTipTitle')}</div>
-            <div className="text-text-tertiary">{t('datasetCreation.stepThree.sideTipContent')}</div>
-          </div>
+      {!isMobile && <div className={cn(s.sideTip)}>
+        <div className={s.tipCard}>
+          <span className={s.icon} />
+          <div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
+          <div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
         </div>
-      )}
+      </div>}
     </div>
   )
 }

+ 30 - 8
web/app/components/datasets/create/step-two/index.module.css

@@ -13,6 +13,18 @@
   z-index: 10;
 }
 
+.form {
+  @apply px-16 pb-8;
+}
+
+.form .label {
+  @apply pt-6 pb-2 flex items-center;
+  font-weight: 500;
+  font-size: 16px;
+  line-height: 24px;
+  color: #344054;
+}
+
 .segmentationItem {
   min-height: 68px;
 }
@@ -63,10 +75,6 @@
   cursor: pointer;
 }
 
-.disabled {
-  cursor: not-allowed !important;
-}
-
 .indexItem.disabled:hover {
   background-color: #fcfcfd;
   border-color: #f2f4f7;
@@ -79,7 +87,8 @@
 }
 
 .radioItem {
-  @apply relative mb-2 rounded-xl border border-components-option-card-option-border cursor-pointer bg-components-option-card-option-bg;
+  @apply relative mb-2 rounded-xl border border-gray-100 cursor-pointer;
+  background-color: #fcfcfd;
 }
 
 .radioItem.segmentationItem.custom {
@@ -137,7 +146,7 @@
 }
 
 .typeIcon.economical {
-  background-image: url(../assets/piggy-bank-mod.svg);
+  background-image: url(../assets/piggy-bank-01.svg);
 }
 
 .radioItem .radio {
@@ -238,7 +247,7 @@
 }
 
 .ruleItem {
-  @apply flex items-center py-1.5;
+  @apply flex items-center;
 }
 
 .formFooter {
@@ -385,6 +394,19 @@
   max-width: 524px;
 }
 
+.previewHeader {
+  position: sticky;
+  top: 0;
+  left: 0;
+  padding-top: 42px;
+  background-color: #fff;
+  font-weight: 600;
+  font-size: 18px;
+  line-height: 28px;
+  color: #101828;
+  z-index: 10;
+}
+
 /* 
  * `fixed` must under `previewHeader` because of style override would not work
  */
@@ -410,4 +432,4 @@
     font-size: 12px;
     line-height: 18px;
   }
-}
+}

+ 692 - 843
web/app/components/datasets/create/step-two/index.tsx

@@ -1,80 +1,65 @@
 'use client'
-import type { FC, PropsWithChildren } from 'react'
-import React, { useCallback, useEffect, useRef, useState } from 'react'
+import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
+import { useBoolean } from 'ahooks'
+import { XMarkIcon } from '@heroicons/react/20/solid'
+import { RocketLaunchIcon } from '@heroicons/react/24/outline'
 import {
-  RiAlertFill,
-  RiArrowLeftLine,
-  RiSearchEyeLine,
+  RiCloseLine,
 } from '@remixicon/react'
 import Link from 'next/link'
-import Image from 'next/image'
-import { useHover } from 'ahooks'
-import SettingCog from '../assets/setting-gear-mod.svg'
-import OrangeEffect from '../assets/option-card-effect-orange.svg'
-import FamilyMod from '../assets/family-mod.svg'
-import Note from '../assets/note-mod.svg'
-import FileList from '../assets/file-list-3-fill.svg'
-import { indexMethodIcon } from '../icons'
-import { PreviewContainer } from '../../preview/container'
-import { ChunkContainer, QAPreview } from '../../chunk'
-import { PreviewHeader } from '../../preview/header'
-import { FormattedText } from '../../formatted-text/formatted'
-import { PreviewSlice } from '../../formatted-text/flavours/preview-slice'
-import PreviewDocumentPicker from '../../common/document-picker/preview-document-picker'
+import { groupBy } from 'lodash-es'
+import PreviewItem, { PreviewType } from './preview-item'
+import LanguageSelect from './language-select'
 import s from './index.module.css'
 import unescape from './unescape'
 import escape from './escape'
-import { OptionCard } from './option-card'
-import LanguageSelect from './language-select'
-import { DelimiterInput, MaxLengthInput, OverlapInput } from './inputs'
 import cn from '@/utils/classnames'
-import type { CrawlOptions, CrawlResultItem, CreateDocumentReq, CustomFile, DocumentItem, FullDocumentDetail, ParentMode, PreProcessingRule, ProcessRule, Rules, createDocumentResponse } from '@/models/datasets'
-
+import type { CrawlOptions, CrawlResultItem, CreateDocumentReq, CustomFile, FileIndexingEstimateResponse, FullDocumentDetail, IndexingEstimateParams, NotionInfo, PreProcessingRule, ProcessRule, Rules, createDocumentResponse } from '@/models/datasets'
+import {
+  createDocument,
+  createFirstDocument,
+  fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
+  fetchDefaultProcessRule,
+} from '@/service/datasets'
 import Button from '@/app/components/base/button'
+import Input from '@/app/components/base/input'
+import Loading from '@/app/components/base/loading'
 import FloatRightContainer from '@/app/components/base/float-right-container'
 import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
 import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
 import { type RetrievalConfig } from '@/types/app'
 import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
 import Toast from '@/app/components/base/toast'
+import { formatNumber } from '@/utils/format'
 import type { NotionPage } from '@/models/common'
 import { DataSourceProvider } from '@/models/common'
-import { ChunkingMode, DataSourceType, RerankingModeEnum } from '@/models/datasets'
+import { DataSourceType, DocForm } from '@/models/datasets'
+import NotionIcon from '@/app/components/base/notion-icon'
+import Switch from '@/app/components/base/switch'
+import { MessageChatSquare } from '@/app/components/base/icons/src/public/common'
 import { useDatasetDetailContext } from '@/context/dataset-detail'
 import I18n from '@/context/i18n'
+import { IS_CE_EDITION } from '@/config'
 import { RETRIEVE_METHOD } from '@/types/app'
 import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import Tooltip from '@/app/components/base/tooltip'
 import { useDefaultModel, useModelList, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
 import { LanguagesSupported } from '@/i18n/language'
 import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
 import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
 import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
-import Checkbox from '@/app/components/base/checkbox'
-import RadioCard from '@/app/components/base/radio-card'
-import { IS_CE_EDITION } from '@/config'
-import Divider from '@/app/components/base/divider'
-import { getNotionInfo, getWebsiteInfo, useCreateDocument, useCreateFirstDocument, useFetchDefaultProcessRule, useFetchFileIndexingEstimateForFile, useFetchFileIndexingEstimateForNotion, useFetchFileIndexingEstimateForWeb } from '@/service/knowledge/use-create-dataset'
-import Badge from '@/app/components/base/badge'
-import { SkeletonContainer, SkeletonPoint, SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
-import Tooltip from '@/app/components/base/tooltip'
-import CustomDialog from '@/app/components/base/dialog'
-import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
-import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
-
-const TextLabel: FC<PropsWithChildren> = (props) => {
-  return <label className='text-text-secondary system-sm-semibold'>{props.children}</label>
-}
+import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
 
+type ValueOf<T> = T[keyof T]
 type StepTwoProps = {
   isSetting?: boolean
   documentDetail?: FullDocumentDetail
   isAPIKeySet: boolean
   onSetting: () => void
   datasetId?: string
-  indexingType?: IndexingType
-  retrievalMethod?: string
+  indexingType?: ValueOf<IndexingType>
   dataSourceType: DataSourceType
   files: CustomFile[]
   notionPages?: NotionPage[]
@@ -84,48 +69,21 @@ type StepTwoProps = {
   websiteCrawlJobId?: string
   onStepChange?: (delta: number) => void
   updateIndexingTypeCache?: (type: string) => void
-  updateRetrievalMethodCache?: (method: string) => void
   updateResultCache?: (res: createDocumentResponse) => void
   onSave?: () => void
   onCancel?: () => void
 }
 
-export enum SegmentType {
+enum SegmentType {
   AUTO = 'automatic',
   CUSTOM = 'custom',
 }
-export enum IndexingType {
+enum IndexingType {
   QUALIFIED = 'high_quality',
   ECONOMICAL = 'economy',
 }
 
 const DEFAULT_SEGMENT_IDENTIFIER = '\\n\\n'
-const DEFAULT_MAXMIMUM_CHUNK_LENGTH = 500
-const DEFAULT_OVERLAP = 50
-
-type ParentChildConfig = {
-  chunkForContext: ParentMode
-  parent: {
-    delimiter: string
-    maxLength: number
-  }
-  child: {
-    delimiter: string
-    maxLength: number
-  }
-}
-
-const defaultParentChildConfig: ParentChildConfig = {
-  chunkForContext: 'paragraph',
-  parent: {
-    delimiter: '\\n\\n',
-    maxLength: 500,
-  },
-  child: {
-    delimiter: '\\n',
-    maxLength: 200,
-  },
-}
 
 const StepTwo = ({
   isSetting,
@@ -146,7 +104,6 @@ const StepTwo = ({
   updateResultCache,
   onSave,
   onCancel,
-  updateRetrievalMethodCache,
 }: StepTwoProps) => {
   const { t } = useTranslation()
   const { locale } = useContext(I18n)
@@ -154,166 +111,66 @@ const StepTwo = ({
   const isMobile = media === MediaType.mobile
 
   const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
-
-  const isInUpload = Boolean(currentDataset)
-  const isUploadInEmptyDataset = isInUpload && !currentDataset?.doc_form
-  const isNotUploadInEmptyDataset = !isUploadInEmptyDataset
-  const isInInit = !isInUpload && !isSetting
-
   const isInCreatePage = !datasetId || (datasetId && !currentDataset?.data_source_type)
   const dataSourceType = isInCreatePage ? inCreatePageDataSourceType : currentDataset?.data_source_type
-  const [segmentationType, setSegmentationType] = useState<SegmentType>(SegmentType.CUSTOM)
+  const scrollRef = useRef<HTMLDivElement>(null)
+  const [scrolled, setScrolled] = useState(false)
+  const previewScrollRef = useRef<HTMLDivElement>(null)
+  const [previewScrolled, setPreviewScrolled] = useState(false)
+  const [segmentationType, setSegmentationType] = useState<SegmentType>(SegmentType.AUTO)
   const [segmentIdentifier, doSetSegmentIdentifier] = useState(DEFAULT_SEGMENT_IDENTIFIER)
-  const setSegmentIdentifier = useCallback((value: string, canEmpty?: boolean) => {
-    doSetSegmentIdentifier(value ? escape(value) : (canEmpty ? '' : DEFAULT_SEGMENT_IDENTIFIER))
+  const setSegmentIdentifier = useCallback((value: string) => {
+    doSetSegmentIdentifier(value ? escape(value) : DEFAULT_SEGMENT_IDENTIFIER)
   }, [])
-  const [maxChunkLength, setMaxChunkLength] = useState(DEFAULT_MAXMIMUM_CHUNK_LENGTH) // default chunk length
+  const [maxChunkLength, setMaxChunkLength] = useState(4000) // default chunk length
   const [limitMaxChunkLength, setLimitMaxChunkLength] = useState(4000)
-  const [overlap, setOverlap] = useState(DEFAULT_OVERLAP)
+  const [overlap, setOverlap] = useState(50)
   const [rules, setRules] = useState<PreProcessingRule[]>([])
   const [defaultConfig, setDefaultConfig] = useState<Rules>()
   const hasSetIndexType = !!indexingType
-  const [indexType, setIndexType] = useState<IndexingType>(
+  const [indexType, setIndexType] = useState<ValueOf<IndexingType>>(
     (indexingType
       || isAPIKeySet)
       ? IndexingType.QUALIFIED
       : IndexingType.ECONOMICAL,
   )
-
-  const [previewFile, setPreviewFile] = useState<DocumentItem>(
-    (datasetId && documentDetail)
-      ? documentDetail.file
-      : files[0],
-  )
-  const [previewNotionPage, setPreviewNotionPage] = useState<NotionPage>(
-    (datasetId && documentDetail)
-      ? documentDetail.notion_page
-      : notionPages[0],
-  )
-
-  const [previewWebsitePage, setPreviewWebsitePage] = useState<CrawlResultItem>(
-    (datasetId && documentDetail)
-      ? documentDetail.website_page
-      : websitePages[0],
-  )
-
-  // QA Related
-  const [isLanguageSelectDisabled, _setIsLanguageSelectDisabled] = useState(false)
-  const [isQAConfirmDialogOpen, setIsQAConfirmDialogOpen] = useState(false)
-  const [docForm, setDocForm] = useState<ChunkingMode>(
-    (datasetId && documentDetail) ? documentDetail.doc_form as ChunkingMode : ChunkingMode.text,
+  const [isLanguageSelectDisabled, setIsLanguageSelectDisabled] = useState(false)
+  const [docForm, setDocForm] = useState<DocForm | string>(
+    (datasetId && documentDetail) ? documentDetail.doc_form : DocForm.TEXT,
   )
-  const handleChangeDocform = (value: ChunkingMode) => {
-    if (value === ChunkingMode.qa && indexType === IndexingType.ECONOMICAL) {
-      setIsQAConfirmDialogOpen(true)
-      return
-    }
-    if (value === ChunkingMode.parentChild && indexType === IndexingType.ECONOMICAL)
-      setIndexType(IndexingType.QUALIFIED)
-    setDocForm(value)
-    // eslint-disable-next-line @typescript-eslint/no-use-before-define
-    currentEstimateMutation.reset()
-  }
-
   const [docLanguage, setDocLanguage] = useState<string>(
     (datasetId && documentDetail) ? documentDetail.doc_language : (locale !== LanguagesSupported[1] ? 'English' : 'Chinese'),
   )
+  const [QATipHide, setQATipHide] = useState(false)
+  const [previewSwitched, setPreviewSwitched] = useState(false)
+  const [showPreview, { setTrue: setShowPreview, setFalse: hidePreview }] = useBoolean()
+  const [customFileIndexingEstimate, setCustomFileIndexingEstimate] = useState<FileIndexingEstimateResponse | null>(null)
+  const [automaticFileIndexingEstimate, setAutomaticFileIndexingEstimate] = useState<FileIndexingEstimateResponse | null>(null)
 
-  const [parentChildConfig, setParentChildConfig] = useState<ParentChildConfig>(defaultParentChildConfig)
+  const fileIndexingEstimate = (() => {
+    return segmentationType === SegmentType.AUTO ? automaticFileIndexingEstimate : customFileIndexingEstimate
+  })()
+  const [isCreating, setIsCreating] = useState(false)
 
-  const getIndexing_technique = () => indexingType || indexType
-  const currentDocForm = currentDataset?.doc_form || docForm
+  const scrollHandle = (e: Event) => {
+    if ((e.target as HTMLDivElement).scrollTop > 0)
+      setScrolled(true)
 
-  const getProcessRule = (): ProcessRule => {
-    if (currentDocForm === ChunkingMode.parentChild) {
-      return {
-        rules: {
-          pre_processing_rules: rules,
-          segmentation: {
-            separator: unescape(
-              parentChildConfig.parent.delimiter,
-            ),
-            max_tokens: parentChildConfig.parent.maxLength,
-          },
-          parent_mode: parentChildConfig.chunkForContext,
-          subchunk_segmentation: {
-            separator: unescape(parentChildConfig.child.delimiter),
-            max_tokens: parentChildConfig.child.maxLength,
-          },
-        },
-        mode: 'hierarchical',
-      } as ProcessRule
-    }
-    return {
-      rules: {
-        pre_processing_rules: rules,
-        segmentation: {
-          separator: unescape(segmentIdentifier),
-          max_tokens: maxChunkLength,
-          chunk_overlap: overlap,
-        },
-      }, // api will check this. It will be removed after api refactored.
-      mode: segmentationType,
-    } as ProcessRule
+    else
+      setScrolled(false)
   }
 
-  const fileIndexingEstimateQuery = useFetchFileIndexingEstimateForFile({
-    docForm: currentDocForm,
-    docLanguage,
-    dataSourceType: DataSourceType.FILE,
-    files: previewFile
-      ? [files.find(file => file.name === previewFile.name)!]
-      : files,
-    indexingTechnique: getIndexing_technique() as any,
-    processRule: getProcessRule(),
-    dataset_id: datasetId!,
-  })
-  const notionIndexingEstimateQuery = useFetchFileIndexingEstimateForNotion({
-    docForm: currentDocForm,
-    docLanguage,
-    dataSourceType: DataSourceType.NOTION,
-    notionPages: [previewNotionPage],
-    indexingTechnique: getIndexing_technique() as any,
-    processRule: getProcessRule(),
-    dataset_id: datasetId || '',
-  })
-
-  const websiteIndexingEstimateQuery = useFetchFileIndexingEstimateForWeb({
-    docForm: currentDocForm,
-    docLanguage,
-    dataSourceType: DataSourceType.WEB,
-    websitePages: [previewWebsitePage],
-    crawlOptions,
-    websiteCrawlProvider,
-    websiteCrawlJobId,
-    indexingTechnique: getIndexing_technique() as any,
-    processRule: getProcessRule(),
-    dataset_id: datasetId || '',
-  })
-
-  const currentEstimateMutation = dataSourceType === DataSourceType.FILE
-    ? fileIndexingEstimateQuery
-    : dataSourceType === DataSourceType.NOTION
-      ? notionIndexingEstimateQuery
-      : websiteIndexingEstimateQuery
-
-  const fetchEstimate = useCallback(() => {
-    if (dataSourceType === DataSourceType.FILE)
-      fileIndexingEstimateQuery.mutate()
-
-    if (dataSourceType === DataSourceType.NOTION)
-      notionIndexingEstimateQuery.mutate()
+  const previewScrollHandle = (e: Event) => {
+    if ((e.target as HTMLDivElement).scrollTop > 0)
+      setPreviewScrolled(true)
 
-    if (dataSourceType === DataSourceType.WEB)
-      websiteIndexingEstimateQuery.mutate()
-  }, [dataSourceType, fileIndexingEstimateQuery, notionIndexingEstimateQuery, websiteIndexingEstimateQuery])
-
-  const estimate
-    = dataSourceType === DataSourceType.FILE
-      ? fileIndexingEstimateQuery.data
-      : dataSourceType === DataSourceType.NOTION
-        ? notionIndexingEstimateQuery.data
-        : websiteIndexingEstimateQuery.data
+    else
+      setPreviewScrolled(false)
+  }
+  const getFileName = (name: string) => {
+    const arr = name.split('.')
+    return arr.slice(0, -1).join('.')
+  }
 
   const getRuleName = (key: string) => {
     if (key === 'remove_extra_spaces')
@@ -341,20 +198,128 @@ const StepTwo = ({
     if (defaultConfig) {
       setSegmentIdentifier(defaultConfig.segmentation.separator)
       setMaxChunkLength(defaultConfig.segmentation.max_tokens)
-      setOverlap(defaultConfig.segmentation.chunk_overlap!)
+      setOverlap(defaultConfig.segmentation.chunk_overlap)
       setRules(defaultConfig.pre_processing_rules)
     }
-    setParentChildConfig(defaultParentChildConfig)
   }
 
-  const updatePreview = () => {
-    if (segmentationType === SegmentType.CUSTOM && maxChunkLength > 4000) {
-      Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
+  const fetchFileIndexingEstimate = async (docForm = DocForm.TEXT, language?: string) => {
+    // eslint-disable-next-line @typescript-eslint/no-use-before-define
+    const res = await didFetchFileIndexingEstimate(getFileIndexingEstimateParams(docForm, language)!)
+    if (segmentationType === SegmentType.CUSTOM)
+      setCustomFileIndexingEstimate(res)
+    else
+      setAutomaticFileIndexingEstimate(res)
+  }
+
+  const confirmChangeCustomConfig = () => {
+    if (segmentationType === SegmentType.CUSTOM && maxChunkLength > limitMaxChunkLength) {
+      Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck', { limit: limitMaxChunkLength }) })
       return
     }
-    fetchEstimate()
+    setCustomFileIndexingEstimate(null)
+    setShowPreview()
+    fetchFileIndexingEstimate()
+    setPreviewSwitched(false)
+  }
+
+  const getIndexing_technique = () => indexingType || indexType
+
+  const getProcessRule = () => {
+    const processRule: ProcessRule = {
+      rules: {} as any, // api will check this. It will be removed after api refactored.
+      mode: segmentationType,
+    }
+    if (segmentationType === SegmentType.CUSTOM) {
+      const ruleObj = {
+        pre_processing_rules: rules,
+        segmentation: {
+          separator: unescape(segmentIdentifier),
+          max_tokens: maxChunkLength,
+          chunk_overlap: overlap,
+        },
+      }
+      processRule.rules = ruleObj
+    }
+    return processRule
+  }
+
+  const getNotionInfo = () => {
+    const workspacesMap = groupBy(notionPages, 'workspace_id')
+    const workspaces = Object.keys(workspacesMap).map((workspaceId) => {
+      return {
+        workspaceId,
+        pages: workspacesMap[workspaceId],
+      }
+    })
+    return workspaces.map((workspace) => {
+      return {
+        workspace_id: workspace.workspaceId,
+        pages: workspace.pages.map((page) => {
+          const { page_id, page_name, page_icon, type } = page
+          return {
+            page_id,
+            page_name,
+            page_icon,
+            type,
+          }
+        }),
+      }
+    }) as NotionInfo[]
+  }
+
+  const getWebsiteInfo = () => {
+    return {
+      provider: websiteCrawlProvider,
+      job_id: websiteCrawlJobId,
+      urls: websitePages.map(page => page.source_url),
+      only_main_content: crawlOptions?.only_main_content,
+    }
   }
 
+  const getFileIndexingEstimateParams = (docForm: DocForm, language?: string): IndexingEstimateParams | undefined => {
+    if (dataSourceType === DataSourceType.FILE) {
+      return {
+        info_list: {
+          data_source_type: dataSourceType,
+          file_info_list: {
+            file_ids: files.map(file => file.id) as string[],
+          },
+        },
+        indexing_technique: getIndexing_technique() as string,
+        process_rule: getProcessRule(),
+        doc_form: docForm,
+        doc_language: language || docLanguage,
+        dataset_id: datasetId as string,
+      }
+    }
+    if (dataSourceType === DataSourceType.NOTION) {
+      return {
+        info_list: {
+          data_source_type: dataSourceType,
+          notion_info_list: getNotionInfo(),
+        },
+        indexing_technique: getIndexing_technique() as string,
+        process_rule: getProcessRule(),
+        doc_form: docForm,
+        doc_language: language || docLanguage,
+        dataset_id: datasetId as string,
+      }
+    }
+    if (dataSourceType === DataSourceType.WEB) {
+      return {
+        info_list: {
+          data_source_type: dataSourceType,
+          website_info_list: getWebsiteInfo(),
+        },
+        indexing_technique: getIndexing_technique() as string,
+        process_rule: getProcessRule(),
+        doc_form: docForm,
+        doc_language: language || docLanguage,
+        dataset_id: datasetId as string,
+      }
+    }
+  }
   const {
     modelList: rerankModelList,
     defaultModel: rerankDefaultModel,
@@ -386,14 +351,13 @@ const StepTwo = ({
     if (isSetting) {
       params = {
         original_document_id: documentDetail?.id,
-        doc_form: currentDocForm,
+        doc_form: docForm,
         doc_language: docLanguage,
         process_rule: getProcessRule(),
         // eslint-disable-next-line @typescript-eslint/no-use-before-define
         retrieval_model: retrievalConfig, // Readonly. If want to changed, just go to settings page.
         embedding_model: embeddingModel.model, // Readonly
         embedding_model_provider: embeddingModel.provider, // Readonly
-        indexing_technique: getIndexing_technique(),
       } as CreateDocumentReq
     }
     else { // create
@@ -413,12 +377,8 @@ const StepTwo = ({
       }
       const postRetrievalConfig = ensureRerankModelSelected({
         rerankDefaultModel: rerankDefaultModel!,
-        retrievalConfig: {
-          // eslint-disable-next-line @typescript-eslint/no-use-before-define
-          ...retrievalConfig,
-          // eslint-disable-next-line @typescript-eslint/no-use-before-define
-          reranking_enable: retrievalConfig.reranking_mode === RerankingModeEnum.RerankingModel,
-        },
+        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        retrievalConfig,
         indexMethod: indexMethod as string,
       })
       params = {
@@ -430,7 +390,7 @@ const StepTwo = ({
         },
         indexing_technique: getIndexing_technique(),
         process_rule: getProcessRule(),
-        doc_form: currentDocForm,
+        doc_form: docForm,
         doc_language: docLanguage,
 
         retrieval_model: postRetrievalConfig,
@@ -443,36 +403,29 @@ const StepTwo = ({
         }
       }
       if (dataSourceType === DataSourceType.NOTION)
-        params.data_source.info_list.notion_info_list = getNotionInfo(notionPages)
+        params.data_source.info_list.notion_info_list = getNotionInfo()
 
-      if (dataSourceType === DataSourceType.WEB) {
-        params.data_source.info_list.website_info_list = getWebsiteInfo({
-          websiteCrawlProvider,
-          websiteCrawlJobId,
-          websitePages,
-        })
-      }
+      if (dataSourceType === DataSourceType.WEB)
+        params.data_source.info_list.website_info_list = getWebsiteInfo()
     }
     return params
   }
 
-  const fetchDefaultProcessRuleMutation = useFetchDefaultProcessRule({
-    onSuccess(data) {
-      const separator = data.rules.segmentation.separator
+  const getRules = async () => {
+    try {
+      const res = await fetchDefaultProcessRule({ url: '/datasets/process-rule' })
+      const separator = res.rules.segmentation.separator
       setSegmentIdentifier(separator)
-      setMaxChunkLength(data.rules.segmentation.max_tokens)
-      setOverlap(data.rules.segmentation.chunk_overlap!)
-      setRules(data.rules.pre_processing_rules)
-      setDefaultConfig(data.rules)
-      setLimitMaxChunkLength(data.limits.indexing_max_segmentation_tokens_length)
-    },
-    onError(error) {
-      Toast.notify({
-        type: 'error',
-        message: `${error}`,
-      })
-    },
-  })
+      setMaxChunkLength(res.rules.segmentation.max_tokens)
+      setLimitMaxChunkLength(res.limits.indexing_max_segmentation_tokens_length)
+      setOverlap(res.rules.segmentation.chunk_overlap)
+      setRules(res.rules.pre_processing_rules)
+      setDefaultConfig(res.rules)
+    }
+    catch (err) {
+      console.log(err)
+    }
+  }
 
   const getRulesFromDetail = () => {
     if (documentDetail) {
@@ -482,7 +435,7 @@ const StepTwo = ({
       const overlap = rules.segmentation.chunk_overlap
       setSegmentIdentifier(separator)
       setMaxChunkLength(max)
-      setOverlap(overlap!)
+      setOverlap(overlap)
       setRules(rules.pre_processing_rules)
       setDefaultConfig(rules)
     }
@@ -490,81 +443,119 @@ const StepTwo = ({
 
   const getDefaultMode = () => {
     if (documentDetail)
-      // @ts-expect-error fix after api refactored
       setSegmentationType(documentDetail.dataset_process_rule.mode)
   }
 
-  const createFirstDocumentMutation = useCreateFirstDocument({
-    onError(error) {
-      Toast.notify({
-        type: 'error',
-        message: `${error}`,
-      })
-    },
-  })
-  const createDocumentMutation = useCreateDocument(datasetId!, {
-    onError(error) {
+  const createHandle = async () => {
+    if (isCreating)
+      return
+    setIsCreating(true)
+    try {
+      let res
+      const params = getCreationParams()
+      if (!params)
+        return false
+
+      setIsCreating(true)
+      if (!datasetId) {
+        res = await createFirstDocument({
+          body: params as CreateDocumentReq,
+        })
+        updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
+        updateResultCache && updateResultCache(res)
+      }
+      else {
+        res = await createDocument({
+          datasetId,
+          body: params as CreateDocumentReq,
+        })
+        updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
+        updateResultCache && updateResultCache(res)
+      }
+      if (mutateDatasetRes)
+        mutateDatasetRes()
+      onStepChange && onStepChange(+1)
+      isSetting && onSave && onSave()
+    }
+    catch (err) {
       Toast.notify({
         type: 'error',
-        message: `${error}`,
+        message: `${err}`,
       })
-    },
-  })
-
-  const isCreating = createFirstDocumentMutation.isPending || createDocumentMutation.isPending
+    }
+    finally {
+      setIsCreating(false)
+    }
+  }
 
-  const createHandle = async () => {
-    const params = getCreationParams()
-    if (!params)
-      return false
+  const handleSwitch = (state: boolean) => {
+    if (state)
+      setDocForm(DocForm.QA)
+    else
+      setDocForm(DocForm.TEXT)
+  }
 
-    if (!datasetId) {
-      await createFirstDocumentMutation.mutateAsync(
-        params,
-        {
-          onSuccess(data) {
-            updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
-            updateResultCache && updateResultCache(data)
-            // eslint-disable-next-line @typescript-eslint/no-use-before-define
-            updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string)
-          },
-        },
-      )
+  const previewSwitch = async (language?: string) => {
+    setPreviewSwitched(true)
+    setIsLanguageSelectDisabled(true)
+    if (segmentationType === SegmentType.AUTO)
+      setAutomaticFileIndexingEstimate(null)
+    else
+      setCustomFileIndexingEstimate(null)
+    try {
+      await fetchFileIndexingEstimate(DocForm.QA, language)
     }
-    else {
-      await createDocumentMutation.mutateAsync(params, {
-        onSuccess(data) {
-          updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
-          updateResultCache && updateResultCache(data)
-        },
-      })
+    finally {
+      setIsLanguageSelectDisabled(false)
     }
-    if (mutateDatasetRes)
-      mutateDatasetRes()
-    onStepChange && onStepChange(+1)
-    isSetting && onSave && onSave()
   }
 
-  const changeToEconomicalType = () => {
-    if (docForm !== ChunkingMode.text)
-      return
+  const handleSelect = (language: string) => {
+    setDocLanguage(language)
+    // Switch language, re-cutter
+    if (docForm === DocForm.QA && previewSwitched)
+      previewSwitch(language)
+  }
 
-    if (!hasSetIndexType)
+  const changeToEconomicalType = () => {
+    if (!hasSetIndexType) {
       setIndexType(IndexingType.ECONOMICAL)
+      setDocForm(DocForm.TEXT)
+    }
   }
 
   useEffect(() => {
     // fetch rules
     if (!isSetting) {
-      fetchDefaultProcessRuleMutation.mutate('/datasets/process-rule')
+      getRules()
     }
     else {
       getRulesFromDetail()
       getDefaultMode()
     }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [])
 
+  useEffect(() => {
+    scrollRef.current?.addEventListener('scroll', scrollHandle)
+    return () => {
+      scrollRef.current?.removeEventListener('scroll', scrollHandle)
+    }
+  }, [])
+
+  useLayoutEffect(() => {
+    if (showPreview) {
+      previewScrollRef.current?.addEventListener('scroll', previewScrollHandle)
+      return () => {
+        previewScrollRef.current?.removeEventListener('scroll', previewScrollHandle)
+      }
+    }
+  }, [showPreview])
+
+  useEffect(() => {
+    if (indexingType === IndexingType.ECONOMICAL && docForm === DocForm.QA)
+      setDocForm(DocForm.TEXT)
+  }, [indexingType, docForm])
+
   useEffect(() => {
     // get indexing type by props
     if (indexingType)
@@ -574,6 +565,20 @@ const StepTwo = ({
       setIndexType(isAPIKeySet ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL)
   }, [isAPIKeySet, indexingType, datasetId])
 
+  useEffect(() => {
+    if (segmentationType === SegmentType.AUTO) {
+      setAutomaticFileIndexingEstimate(null)
+      !isMobile && setShowPreview()
+      fetchFileIndexingEstimate()
+      setPreviewSwitched(false)
+    }
+    else {
+      hidePreview()
+      setCustomFileIndexingEstimate(null)
+      setPreviewSwitched(false)
+    }
+  }, [segmentationType, indexType])
+
   const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict || {
     search_method: RETRIEVE_METHOD.semantic,
     reranking_enable: false,
@@ -586,589 +591,433 @@ const StepTwo = ({
     score_threshold: 0.5,
   } as RetrievalConfig)
 
-  const economyDomRef = useRef<HTMLDivElement>(null)
-  const isHoveringEconomy = useHover(economyDomRef)
-
   return (
     <div className='flex w-full h-full'>
-      <div className={cn('relative h-full w-1/2 py-6 overflow-y-auto', isMobile ? 'px-4' : 'px-12')}>
-        <div className={'system-md-semibold mb-1'}>{t('datasetCreation.stepTwo.segmentation')}</div>
-        {((isInUpload && [ChunkingMode.text, ChunkingMode.qa].includes(currentDataset!.doc_form))
-          || isUploadInEmptyDataset
-          || isInInit)
-          && <OptionCard
-            className='bg-background-section mb-2'
-            title={t('datasetCreation.stepTwo.general')}
-            icon={<Image width={20} height={20} src={SettingCog} alt={t('datasetCreation.stepTwo.general')} />}
-            activeHeaderClassName='bg-dataset-option-card-blue-gradient'
-            description={t('datasetCreation.stepTwo.generalTip')}
-            isActive={
-              [ChunkingMode.text, ChunkingMode.qa].includes(currentDocForm)
-            }
-            onSwitched={() =>
-              handleChangeDocform(ChunkingMode.text)
-            }
-            actions={
-              <>
-                <Button variant={'secondary-accent'} onClick={() => updatePreview()}>
-                  <RiSearchEyeLine className='h-4 w-4 mr-0.5' />
-                  {t('datasetCreation.stepTwo.previewChunk')}
-                </Button>
-                <Button variant={'ghost'} onClick={resetRules}>
-                  {t('datasetCreation.stepTwo.reset')}
-                </Button>
-              </>
-            }
-            noHighlight={isInUpload && isNotUploadInEmptyDataset}
-          >
-            <div className='flex flex-col gap-y-4'>
-              <div className='flex gap-3'>
-                <DelimiterInput
-                  value={segmentIdentifier}
-                  onChange={e => setSegmentIdentifier(e.target.value, true)}
-                />
-                <MaxLengthInput
-                  unit='tokens'
-                  value={maxChunkLength}
-                  onChange={setMaxChunkLength}
-                />
-                <OverlapInput
-                  unit='tokens'
-                  value={overlap}
-                  min={1}
-                  onChange={setOverlap}
-                />
+      <div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
+        <div className={cn(s.pageHeader, scrolled && s.fixed, isMobile && '!px-6')}>
+          <span>{t('datasetCreation.steps.two')}</span>
+          {(isMobile || !showPreview) && (
+            <Button
+              className='border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
+              onClick={setShowPreview}
+            >
+              <Tooltip>
+                <div className="flex flex-row items-center">
+                  <RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
+                  <span className="text-[13px]">{t('datasetCreation.stepTwo.previewTitleButton')}</span>
+                </div>
+              </Tooltip>
+            </Button>
+          )}
+        </div>
+        <div className={cn(s.form, isMobile && '!px-4')}>
+          <div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
+          <div className='max-w-[640px]'>
+            <div
+              className={cn(
+                s.radioItem,
+                s.segmentationItem,
+                segmentationType === SegmentType.AUTO && s.active,
+              )}
+              onClick={() => setSegmentationType(SegmentType.AUTO)}
+            >
+              <span className={cn(s.typeIcon, s.auto)} />
+              <span className={cn(s.radio)} />
+              <div className={s.typeHeader}>
+                <div className={s.title}>{t('datasetCreation.stepTwo.auto')}</div>
+                <div className={s.tip}>{t('datasetCreation.stepTwo.autoDescription')}</div>
               </div>
-              <div className='w-full flex flex-col'>
-                <div className='flex items-center gap-x-2'>
-                  <div className='inline-flex shrink-0'>
-                    <TextLabel>{t('datasetCreation.stepTwo.rules')}</TextLabel>
+            </div>
+            <div
+              className={cn(
+                s.radioItem,
+                s.segmentationItem,
+                segmentationType === SegmentType.CUSTOM && s.active,
+                segmentationType === SegmentType.CUSTOM && s.custom,
+              )}
+              onClick={() => setSegmentationType(SegmentType.CUSTOM)}
+            >
+              <span className={cn(s.typeIcon, s.customize)} />
+              <span className={cn(s.radio)} />
+              <div className={s.typeHeader}>
+                <div className={s.title}>{t('datasetCreation.stepTwo.custom')}</div>
+                <div className={s.tip}>{t('datasetCreation.stepTwo.customDescription')}</div>
+              </div>
+              {segmentationType === SegmentType.CUSTOM && (
+                <div className={s.typeFormBody}>
+                  <div className={s.formRow}>
+                    <div className='w-full'>
+                      <div className={s.label}>
+                        {t('datasetCreation.stepTwo.separator')}
+                        <Tooltip
+                          popupContent={
+                            <div className='max-w-[200px]'>
+                              {t('datasetCreation.stepTwo.separatorTip')}
+                            </div>
+                          }
+                        />
+                      </div>
+                      <Input
+                        type="text"
+                        className='h-9'
+                        placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier}
+                        onChange={e => setSegmentIdentifier(e.target.value)}
+                      />
+                    </div>
                   </div>
-                  <Divider className='grow' bgStyle='gradient' />
-                </div>
-                <div className='mt-1'>
-                  {rules.map(rule => (
-                    <div key={rule.id} className={s.ruleItem} onClick={() => {
-                      ruleChangeHandle(rule.id)
-                    }}>
-                      <Checkbox
-                        checked={rule.enabled}
+                  <div className={s.formRow}>
+                    <div className='w-full'>
+                      <div className={s.label}>{t('datasetCreation.stepTwo.maxLength')}</div>
+                      <Input
+                        type="number"
+                        className='h-9'
+                        placeholder={t('datasetCreation.stepTwo.maxLength') || ''}
+                        value={maxChunkLength}
+                        max={limitMaxChunkLength}
+                        min={1}
+                        onChange={e => setMaxChunkLength(parseInt(e.target.value.replace(/^0+/, ''), 10))}
                       />
-                      <label className="ml-2 system-sm-regular cursor-pointer text-text-secondary">{getRuleName(rule.id)}</label>
                     </div>
-                  ))}
-                  {IS_CE_EDITION && <>
-                    <Divider type='horizontal' className='my-4 bg-divider-subtle' />
-                    <div className='flex items-center py-0.5'>
-                      <div className='flex items-center' onClick={() => {
-                        if (currentDataset?.doc_form)
-                          return
-                        if (docForm === ChunkingMode.qa)
-                          handleChangeDocform(ChunkingMode.text)
-                        else
-                          handleChangeDocform(ChunkingMode.qa)
-                      }}>
-                        <Checkbox
-                          checked={currentDocForm === ChunkingMode.qa}
-                          disabled={!!currentDataset?.doc_form}
+                  </div>
+                  <div className={s.formRow}>
+                    <div className='w-full'>
+                      <div className={s.label}>
+                        {t('datasetCreation.stepTwo.overlap')}
+                        <Tooltip
+                          popupContent={
+                            <div className='max-w-[200px]'>
+                              {t('datasetCreation.stepTwo.overlapTip')}
+                            </div>
+                          }
                         />
-                        <label className="ml-2 system-sm-regular cursor-pointer text-text-secondary">
-                          {t('datasetCreation.stepTwo.useQALanguage')}
-                        </label>
                       </div>
-                      <LanguageSelect
-                        currentLanguage={docLanguage || locale}
-                        onSelect={setDocLanguage}
-                        disabled={currentDocForm !== ChunkingMode.qa}
+                      <Input
+                        type="number"
+                        className='h-9'
+                        placeholder={t('datasetCreation.stepTwo.overlap') || ''}
+                        value={overlap}
+                        min={1}
+                        onChange={e => setOverlap(parseInt(e.target.value.replace(/^0+/, ''), 10))}
                       />
-                      <Tooltip popupContent={t('datasetCreation.stepTwo.QATip')} />
                     </div>
-                    {currentDocForm === ChunkingMode.qa && (
-                      <div
-                        style={{
-                          background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.1) 0%, rgba(255, 255, 255, 0.00) 100%)',
-                        }}
-                        className='h-10 mt-2 flex items-center gap-2 rounded-xl backdrop-blur-[5px] border-components-panel-border border shadow-xs px-3 text-xs'
-                      >
-                        <RiAlertFill className='size-4 text-text-warning-secondary' />
-                        <span className='system-xs-medium text-text-primary'>
-                          {t('datasetCreation.stepTwo.QATip')}
-                        </span>
-                      </div>
-                    )}
-                  </>}
-                </div>
-              </div>
-            </div>
-          </OptionCard>}
-        {
-          (
-            (isInUpload && currentDataset!.doc_form === ChunkingMode.parentChild)
-            || isUploadInEmptyDataset
-            || isInInit
-          )
-          && <OptionCard
-            title={t('datasetCreation.stepTwo.parentChild')}
-            icon={<Image width={20} height={20} src={FamilyMod} alt={t('datasetCreation.stepTwo.parentChild')} />}
-            effectImg={OrangeEffect.src}
-            activeHeaderClassName='bg-dataset-option-card-orange-gradient'
-            description={t('datasetCreation.stepTwo.parentChildTip')}
-            isActive={currentDocForm === ChunkingMode.parentChild}
-            onSwitched={() => handleChangeDocform(ChunkingMode.parentChild)}
-            actions={
-              <>
-                <Button variant={'secondary-accent'} onClick={() => updatePreview()}>
-                  <RiSearchEyeLine className='h-4 w-4 mr-0.5' />
-                  {t('datasetCreation.stepTwo.previewChunk')}
-                </Button>
-                <Button variant={'ghost'} onClick={resetRules}>
-                  {t('datasetCreation.stepTwo.reset')}
-                </Button>
-              </>
-            }
-            noHighlight={isInUpload && isNotUploadInEmptyDataset}
-          >
-            <div className='flex flex-col gap-4'>
-              <div>
-                <div className='flex items-center gap-x-2'>
-                  <div className='inline-flex shrink-0'>
-                    <TextLabel>{t('datasetCreation.stepTwo.parentChunkForContext')}</TextLabel>
                   </div>
-                  <Divider className='grow' bgStyle='gradient' />
-                </div>
-                <RadioCard className='mt-1'
-                  icon={<Image src={Note} alt='' />}
-                  title={t('datasetCreation.stepTwo.paragraph')}
-                  description={t('datasetCreation.stepTwo.paragraphTip')}
-                  isChosen={parentChildConfig.chunkForContext === 'paragraph'}
-                  onChosen={() => setParentChildConfig(
-                    {
-                      ...parentChildConfig,
-                      chunkForContext: 'paragraph',
-                    },
-                  )}
-                  chosenConfig={
-                    <div className='flex gap-3'>
-                      <DelimiterInput
-                        value={parentChildConfig.parent.delimiter}
-                        tooltip={t('datasetCreation.stepTwo.parentChildDelimiterTip')!}
-                        onChange={e => setParentChildConfig({
-                          ...parentChildConfig,
-                          parent: {
-                            ...parentChildConfig.parent,
-                            delimiter: e.target.value ? escape(e.target.value) : '',
-                          },
-                        })}
-                      />
-                      <MaxLengthInput
-                        unit='tokens'
-                        value={parentChildConfig.parent.maxLength}
-                        onChange={value => setParentChildConfig({
-                          ...parentChildConfig,
-                          parent: {
-                            ...parentChildConfig.parent,
-                            maxLength: value,
-                          },
-                        })}
-                      />
+                  <div className={s.formRow}>
+                    <div className='w-full flex flex-col gap-1'>
+                      <div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
+                      {rules.map(rule => (
+                        <div key={rule.id} className={s.ruleItem}>
+                          <input id={rule.id} type="checkbox" checked={rule.enabled} onChange={() => ruleChangeHandle(rule.id)} className="w-4 h-4 rounded border-gray-300 text-blue-700 focus:ring-blue-700" />
+                          <label htmlFor={rule.id} className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
+                        </div>
+                      ))}
                     </div>
-                  }
-                />
-                <RadioCard className='mt-2'
-                  icon={<Image src={FileList} alt='' />}
-                  title={t('datasetCreation.stepTwo.fullDoc')}
-                  description={t('datasetCreation.stepTwo.fullDocTip')}
-                  onChosen={() => setParentChildConfig(
-                    {
-                      ...parentChildConfig,
-                      chunkForContext: 'full-doc',
-                    },
-                  )}
-                  isChosen={parentChildConfig.chunkForContext === 'full-doc'}
-                />
-              </div>
-
-              <div>
-                <div className='flex items-center gap-x-2'>
-                  <div className='inline-flex shrink-0'>
-                    <TextLabel>{t('datasetCreation.stepTwo.childChunkForRetrieval')}</TextLabel>
                   </div>
-                  <Divider className='grow' bgStyle='gradient' />
-                </div>
-                <div className='flex gap-3 mt-1'>
-                  <DelimiterInput
-                    value={parentChildConfig.child.delimiter}
-                    tooltip={t('datasetCreation.stepTwo.parentChildChunkDelimiterTip')!}
-                    onChange={e => setParentChildConfig({
-                      ...parentChildConfig,
-                      child: {
-                        ...parentChildConfig.child,
-                        delimiter: e.target.value ? escape(e.target.value) : '',
-                      },
-                    })}
-                  />
-                  <MaxLengthInput
-                    unit='tokens'
-                    value={parentChildConfig.child.maxLength}
-                    onChange={value => setParentChildConfig({
-                      ...parentChildConfig,
-                      child: {
-                        ...parentChildConfig.child,
-                        maxLength: value,
-                      },
-                    })}
-                  />
-                </div>
-              </div>
-              <div>
-                <div className='flex items-center gap-x-2'>
-                  <div className='inline-flex shrink-0'>
-                    <TextLabel>{t('datasetCreation.stepTwo.rules')}</TextLabel>
+                  <div className={s.formFooter}>
+                    <Button variant="primary" className={cn(s.button)} onClick={confirmChangeCustomConfig}>{t('datasetCreation.stepTwo.preview')}</Button>
+                    <Button className={cn(s.button, 'ml-2')} onClick={resetRules}>{t('datasetCreation.stepTwo.reset')}</Button>
                   </div>
-                  <Divider className='grow' bgStyle='gradient' />
                 </div>
-                <div className='mt-1'>
-                  {rules.map(rule => (
-                    <div key={rule.id} className={s.ruleItem} onClick={() => {
-                      ruleChangeHandle(rule.id)
-                    }}>
-                      <Checkbox
-                        checked={rule.enabled}
-                      />
-                      <label className="ml-2 system-sm-regular cursor-pointer text-text-secondary">{getRuleName(rule.id)}</label>
-                    </div>
-                  ))}
-                </div>
-              </div>
+              )}
             </div>
-          </OptionCard>}
-        <Divider className='my-5' />
-        <div className={'system-md-semibold mb-1'}>{t('datasetCreation.stepTwo.indexMode')}</div>
-        <div className='flex items-center gap-2'>
-          {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
-            <OptionCard className='flex-1'
-              title={<div className='flex items-center'>
-                {t('datasetCreation.stepTwo.qualified')}
-                <Badge className={cn('ml-1 h-[18px]', (!hasSetIndexType && indexType === IndexingType.QUALIFIED) ? 'border-text-accent-secondary text-text-accent-secondary' : '')} uppercase>
-                  {t('datasetCreation.stepTwo.recommend')}
-                </Badge>
-                <span className='ml-auto'>
+          </div>
+          <div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
+          <div className='max-w-[640px]'>
+            <div className='flex items-center gap-3 flex-wrap sm:flex-nowrap'>
+              {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
+                <div
+                  className={cn(
+                    s.radioItem,
+                    s.indexItem,
+                    !isAPIKeySet && s.disabled,
+                    !hasSetIndexType && indexType === IndexingType.QUALIFIED && s.active,
+                    hasSetIndexType && s.disabled,
+                    hasSetIndexType && '!w-full !min-h-[96px]',
+                  )}
+                  onClick={() => {
+                    if (isAPIKeySet)
+                      setIndexType(IndexingType.QUALIFIED)
+                  }}
+                >
+                  <span className={cn(s.typeIcon, s.qualified)} />
                   {!hasSetIndexType && <span className={cn(s.radio)} />}
-                </span>
-              </div>}
-              description={t('datasetCreation.stepTwo.qualifiedTip')}
-              icon={<Image src={indexMethodIcon.high_quality} alt='' />}
-              isActive={!hasSetIndexType && indexType === IndexingType.QUALIFIED}
-              disabled={!isAPIKeySet || hasSetIndexType}
-              onSwitched={() => {
-                if (isAPIKeySet)
-                  setIndexType(IndexingType.QUALIFIED)
-              }}
-            />
-          )}
-
-          {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.ECONOMICAL)) && (
-            <>
-              <CustomDialog show={isQAConfirmDialogOpen} onClose={() => setIsQAConfirmDialogOpen(false)} className='w-[432px]'>
-                <header className='pt-6 mb-4'>
-                  <h2 className='text-lg font-semibold'>
-                    {t('datasetCreation.stepTwo.qaSwitchHighQualityTipTitle')}
-                  </h2>
-                  <p className='font-normal text-sm mt-2'>
-                    {t('datasetCreation.stepTwo.qaSwitchHighQualityTipContent')}
-                  </p>
-                </header>
-                <div className='flex gap-2 pb-6'>
-                  <Button className='ml-auto' onClick={() => {
-                    setIsQAConfirmDialogOpen(false)
-                  }}>
-                    {t('datasetCreation.stepTwo.cancel')}
-                  </Button>
-                  <Button variant={'primary'} onClick={() => {
-                    setIsQAConfirmDialogOpen(false)
-                    setIndexType(IndexingType.QUALIFIED)
-                    setDocForm(ChunkingMode.qa)
-                  }}>
-                    {t('datasetCreation.stepTwo.switch')}
-                  </Button>
+                  <div className={s.typeHeader}>
+                    <div className={s.title}>
+                      {t('datasetCreation.stepTwo.qualified')}
+                      {!hasSetIndexType && <span className={s.recommendTag}>{t('datasetCreation.stepTwo.recommend')}</span>}
+                    </div>
+                    <div className={s.tip}>{t('datasetCreation.stepTwo.qualifiedTip')}</div>
+                  </div>
+                  {!isAPIKeySet && (
+                    <div className={s.warningTip}>
+                      <span>{t('datasetCreation.stepTwo.warning')}&nbsp;</span>
+                      <span className={s.click} onClick={onSetting}>{t('datasetCreation.stepTwo.click')}</span>
+                    </div>
+                  )}
                 </div>
-              </CustomDialog>
-              <PortalToFollowElem
-                open={
-                  isHoveringEconomy && docForm !== ChunkingMode.text
-                }
-                placement={'top'}
-              >
-                <PortalToFollowElemTrigger asChild>
-                  <OptionCard className='flex-1'
-                    title={t('datasetCreation.stepTwo.economical')}
-                    description={t('datasetCreation.stepTwo.economicalTip')}
-                    icon={<Image src={indexMethodIcon.economical} alt='' />}
-                    isActive={!hasSetIndexType && indexType === IndexingType.ECONOMICAL}
-                    disabled={!isAPIKeySet || hasSetIndexType || docForm !== ChunkingMode.text}
-                    ref={economyDomRef}
-                    onSwitched={() => {
-                      if (isAPIKeySet && docForm === ChunkingMode.text)
-                        setIndexType(IndexingType.ECONOMICAL)
-                    }}
-                  />
-                </PortalToFollowElemTrigger>
-                <PortalToFollowElemContent>
-                  <div className='p-3 bg-components-tooltip-bg border-components-panel-border text-xs font-medium text-text-secondary rounded-lg shadow-lg'>
-                    {
-                      docForm === ChunkingMode.qa
-                        ? t('datasetCreation.stepTwo.notAvailableForQA')
-                        : t('datasetCreation.stepTwo.notAvailableForParentChild')
-                    }
+              )}
+
+              {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.ECONOMICAL)) && (
+                <div
+                  className={cn(
+                    s.radioItem,
+                    s.indexItem,
+                    !hasSetIndexType && indexType === IndexingType.ECONOMICAL && s.active,
+                    hasSetIndexType && s.disabled,
+                    hasSetIndexType && '!w-full !min-h-[96px]',
+                  )}
+                  onClick={changeToEconomicalType}
+                >
+                  <span className={cn(s.typeIcon, s.economical)} />
+                  {!hasSetIndexType && <span className={cn(s.radio)} />}
+                  <div className={s.typeHeader}>
+                    <div className={s.title}>{t('datasetCreation.stepTwo.economical')}</div>
+                    <div className={s.tip}>{t('datasetCreation.stepTwo.economicalTip')}</div>
                   </div>
-                </PortalToFollowElemContent>
-              </PortalToFollowElem>
-            </>)}
-        </div>
-        {!hasSetIndexType && indexType === IndexingType.QUALIFIED && (
-          <div className='mt-2 h-10 p-2 flex items-center gap-x-0.5 rounded-xl border-[0.5px] border-components-panel-border overflow-hidden bg-components-panel-bg-blur backdrop-blur-[5px] shadow-xs'>
-            <div className='absolute top-0 left-0 right-0 bottom-0 bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)] opacity-40'></div>
-            <div className='p-1'>
-              <AlertTriangle className='size-4 text-text-warning-secondary' />
+                </div>
+              )}
             </div>
-            <span className='system-xs-medium'>{t('datasetCreation.stepTwo.highQualityTip')}</span>
-          </div>
-        )}
-        {hasSetIndexType && indexType === IndexingType.ECONOMICAL && (
-          <div className='mt-2 system-xs-medium'>
-            {t('datasetCreation.stepTwo.indexSettingTip')}
-            <Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
-          </div>
-        )}
-        {/* Embedding model */}
-        {indexType === IndexingType.QUALIFIED && (
-          <div className='mt-5'>
-            <div className={cn('system-md-semibold mb-1', datasetId && 'flex justify-between items-center')}>{t('datasetSettings.form.embeddingModel')}</div>
-            <ModelSelector
-              readonly={!!datasetId}
-              defaultModel={embeddingModel}
-              modelList={embeddingModelList}
-              onSelect={(model: DefaultModel) => {
-                setEmbeddingModel(model)
-              }}
-            />
-            {!!datasetId && (
-              <div className='mt-2 system-xs-medium'>
+            {hasSetIndexType && indexType === IndexingType.ECONOMICAL && (
+              <div className='mt-2 text-xs text-gray-500 font-medium'>
                 {t('datasetCreation.stepTwo.indexSettingTip')}
-                <Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
+                <Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
               </div>
             )}
-          </div>
-        )}
-        <Divider className='my-5' />
-        {/* Retrieval Method Config */}
-        <div>
-          {!datasetId
-            ? (
-              <div className={'mb-1'}>
-                <div className='system-md-semibold mb-0.5'>{t('datasetSettings.form.retrievalSetting.title')}</div>
-                <div className='body-xs-regular text-text-tertiary'>
-                  <a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
-                  {t('datasetSettings.form.retrievalSetting.longDescription')}
+            {IS_CE_EDITION && indexType === IndexingType.QUALIFIED && (
+              <div className='mt-3 rounded-xl bg-gray-50 border border-gray-100'>
+                <div className='flex justify-between items-center px-5 py-4'>
+                  <div className='flex justify-center items-center w-8 h-8 rounded-lg bg-indigo-50'>
+                    <MessageChatSquare className='w-4 h-4' />
+                  </div>
+                  <div className='grow mx-3'>
+                    <div className='mb-[2px] text-md font-medium text-gray-900'>{t('datasetCreation.stepTwo.QATitle')}</div>
+                    <div className='inline-flex items-center text-[13px] leading-[18px] text-gray-500'>
+                      <span className='pr-1'>{t('datasetCreation.stepTwo.QALanguage')}</span>
+                      <LanguageSelect currentLanguage={docLanguage} onSelect={handleSelect} disabled={isLanguageSelectDisabled} />
+                    </div>
+                  </div>
+                  <div className='shrink-0'>
+                    <Switch
+                      defaultValue={docForm === DocForm.QA}
+                      onChange={handleSwitch}
+                      size='md'
+                    />
+                  </div>
                 </div>
+                {docForm === DocForm.QA && !QATipHide && (
+                  <div className='flex justify-between items-center px-5 py-2 bg-orange-50 border-t border-amber-100 rounded-b-xl text-[13px] leading-[18px] text-medium text-amber-500'>
+                    {t('datasetCreation.stepTwo.QATip')}
+                    <RiCloseLine className='w-4 h-4 text-gray-500 cursor-pointer' onClick={() => setQATipHide(true)} />
+                  </div>
+                )}
               </div>
-            )
-            : (
-              <div className={cn('system-md-semibold mb-0.5', 'flex justify-between items-center')}>
-                <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+            )}
+            {/* Embedding model */}
+            {indexType === IndexingType.QUALIFIED && (
+              <div className='mb-2'>
+                <div className={cn(s.label, datasetId && 'flex justify-between items-center')}>{t('datasetSettings.form.embeddingModel')}</div>
+                <ModelSelector
+                  readonly={!!datasetId}
+                  defaultModel={embeddingModel}
+                  modelList={embeddingModelList}
+                  onSelect={(model: DefaultModel) => {
+                    setEmbeddingModel(model)
+                  }}
+                />
+                {!!datasetId && (
+                  <div className='mt-2 text-xs text-gray-500 font-medium'>
+                    {t('datasetCreation.stepTwo.indexSettingTip')}
+                    <Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
+                  </div>
+                )}
               </div>
             )}
-
-          <div className=''>
-            {
-              getIndexing_technique() === IndexingType.QUALIFIED
+            {/* Retrieval Method Config */}
+            <div>
+              {!datasetId
                 ? (
-                  <RetrievalMethodConfig
-                    value={retrievalConfig}
-                    onChange={setRetrievalConfig}
-                  />
+                  <div className={s.label}>
+                    <div className='shrink-0 mr-4'>{t('datasetSettings.form.retrievalSetting.title')}</div>
+                    <div className='leading-[18px] text-xs font-normal text-gray-500'>
+                      <a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
+                      {t('datasetSettings.form.retrievalSetting.longDescription')}
+                    </div>
+                  </div>
                 )
                 : (
-                  <EconomicalRetrievalMethodConfig
-                    value={retrievalConfig}
-                    onChange={setRetrievalConfig}
-                  />
-                )
-            }
-          </div>
-        </div>
+                  <div className={cn(s.label, 'flex justify-between items-center')}>
+                    <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+                  </div>
+                )}
 
-        {!isSetting
-          ? (
-            <div className='flex items-center mt-8 py-2'>
-              <Button onClick={() => onStepChange && onStepChange(-1)}>
-                <RiArrowLeftLine className='w-4 h-4 mr-1' />
-                {t('datasetCreation.stepTwo.previousStep')}
-              </Button>
-              <Button className='ml-auto' loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
-            </div>
-          )
-          : (
-            <div className='flex items-center mt-8 py-2'>
-              <Button loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.save')}</Button>
-              <Button className='ml-2' onClick={onCancel}>{t('datasetCreation.stepTwo.cancel')}</Button>
+              <div className='max-w-[640px]'>
+                {
+                  getIndexing_technique() === IndexingType.QUALIFIED
+                    ? (
+                      <RetrievalMethodConfig
+                        value={retrievalConfig}
+                        onChange={setRetrievalConfig}
+                      />
+                    )
+                    : (
+                      <EconomicalRetrievalMethodConfig
+                        value={retrievalConfig}
+                        onChange={setRetrievalConfig}
+                      />
+                    )
+                }
+              </div>
             </div>
-          )}
-      </div>
-      <FloatRightContainer isMobile={isMobile} isOpen={true} onClose={() => { }} footer={null}>
-        <PreviewContainer
-          header={<PreviewHeader
-            title={t('datasetCreation.stepTwo.preview')}
-          >
-            <div className='flex items-center gap-1'>
-              {dataSourceType === DataSourceType.FILE
-                && <PreviewDocumentPicker
-                  files={files as Array<Required<CustomFile>>}
-                  onChange={(selected) => {
-                    currentEstimateMutation.reset()
-                    setPreviewFile(selected)
-                    currentEstimateMutation.mutate()
-                  }}
-                  // when it is from setting, it just has one file
-                  value={isSetting ? (files[0]! as Required<CustomFile>) : previewFile}
-                />
-              }
-              {dataSourceType === DataSourceType.NOTION
-                && <PreviewDocumentPicker
-                  files={
-                    notionPages.map(page => ({
-                      id: page.page_id,
-                      name: page.page_name,
-                      extension: 'md',
-                    }))
-                  }
-                  onChange={(selected) => {
-                    currentEstimateMutation.reset()
-                    const selectedPage = notionPages.find(page => page.page_id === selected.id)
-                    setPreviewNotionPage(selectedPage!)
-                    currentEstimateMutation.mutate()
-                  }}
-                  value={{
-                    id: previewNotionPage?.page_id || '',
-                    name: previewNotionPage?.page_name || '',
-                    extension: 'md',
-                  }}
-                />
-              }
-              {dataSourceType === DataSourceType.WEB
-                && <PreviewDocumentPicker
-                  files={
-                    websitePages.map(page => ({
-                      id: page.source_url,
-                      name: page.title,
-                      extension: 'md',
-                    }))
-                  }
-                  onChange={(selected) => {
-                    currentEstimateMutation.reset()
-                    const selectedPage = websitePages.find(page => page.source_url === selected.id)
-                    setPreviewWebsitePage(selectedPage!)
-                    currentEstimateMutation.mutate()
-                  }}
-                  value={
-                    {
-                      id: previewWebsitePage?.source_url || '',
-                      name: previewWebsitePage?.title || '',
-                      extension: 'md',
-                    }
+
+            <div className={s.source}>
+              <div className={s.sourceContent}>
+                {dataSourceType === DataSourceType.FILE && (
+                  <>
+                    <div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.fileSource')}</div>
+                    <div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
+                      <span className={cn(s.fileIcon, files.length && s[files[0].extension || ''])} />
+                      {getFileName(files[0].name || '')}
+                      {files.length > 1 && (
+                        <span className={s.sourceCount}>
+                          <span>{t('datasetCreation.stepTwo.other')}</span>
+                          <span>{files.length - 1}</span>
+                          <span>{t('datasetCreation.stepTwo.fileUnit')}</span>
+                        </span>
+                      )}
+                    </div>
+                  </>
+                )}
+                {dataSourceType === DataSourceType.NOTION && (
+                  <>
+                    <div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.notionSource')}</div>
+                    <div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
+                      <NotionIcon
+                        className='shrink-0 mr-1'
+                        type='page'
+                        src={notionPages[0]?.page_icon}
+                      />
+                      {notionPages[0]?.page_name}
+                      {notionPages.length > 1 && (
+                        <span className={s.sourceCount}>
+                          <span>{t('datasetCreation.stepTwo.other')}</span>
+                          <span>{notionPages.length - 1}</span>
+                          <span>{t('datasetCreation.stepTwo.notionUnit')}</span>
+                        </span>
+                      )}
+                    </div>
+                  </>
+                )}
+                {dataSourceType === DataSourceType.WEB && (
+                  <>
+                    <div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.websiteSource')}</div>
+                    <div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
+                      <Globe01 className='shrink-0 mr-1' />
+                      <span className='grow w-0 truncate'>{websitePages[0].source_url}</span>
+                      {websitePages.length > 1 && (
+                        <span className={s.sourceCount}>
+                          <span>{t('datasetCreation.stepTwo.other')}</span>
+                          <span>{websitePages.length - 1}</span>
+                          <span>{t('datasetCreation.stepTwo.webpageUnit')}</span>
+                        </span>
+                      )}
+                    </div>
+                  </>
+                )}
+              </div>
+              <div className={s.divider} />
+              <div className={s.segmentCount}>
+                <div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.estimateSegment')}</div>
+                <div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
+                  {
+                    fileIndexingEstimate
+                      ? (
+                        <div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.total_segments)} </div>
+                      )
+                      : (
+                        <div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
+                      )
                   }
-                />
-              }
-              {
-                currentDocForm !== ChunkingMode.qa
-                  && <Badge text={t(
-                    'datasetCreation.stepTwo.previewChunkCount', {
-                      count: estimate?.total_segments || 0,
-                    }) as string}
-                  />
-              }
+                </div>
+              </div>
             </div>
-          </PreviewHeader>}
-          className={cn('flex shrink-0 w-1/2 p-4 pr-0 relative h-full', isMobile && 'w-full max-w-[524px]')}
-          mainClassName='space-y-6'
-        >
-          {currentDocForm === ChunkingMode.qa && estimate?.qa_preview && (
-            estimate?.qa_preview.map((item, index) => (
-              <ChunkContainer
-                key={item.question}
-                label={`Chunk-${index + 1}`}
-                characterCount={item.question.length + item.answer.length}
-              >
-                <QAPreview qa={item} />
-              </ChunkContainer>
-            ))
-          )}
-          {currentDocForm === ChunkingMode.text && estimate?.preview && (
-            estimate?.preview.map((item, index) => (
-              <ChunkContainer
-                key={item.content}
-                label={`Chunk-${index + 1}`}
-                characterCount={item.content.length}
-              >
-                {item.content}
-              </ChunkContainer>
-            ))
-          )}
-          {currentDocForm === ChunkingMode.parentChild && currentEstimateMutation.data?.preview && (
-            estimate?.preview?.map((item, index) => {
-              const indexForLabel = index + 1
-              return (
-                <ChunkContainer
-                  key={item.content}
-                  label={`Chunk-${indexForLabel}`}
-                  characterCount={item.content.length}
-                >
-                  <FormattedText>
-                    {item.child_chunks.map((child, index) => {
-                      const indexForLabel = index + 1
-                      return (
-                        <PreviewSlice
-                          key={child}
-                          label={`C-${indexForLabel}`}
-                          text={child}
-                          tooltip={`Child-chunk-${indexForLabel} · ${child.length} Characters`}
-                          labelInnerClassName='text-[10px] font-semibold align-bottom leading-7'
-                          dividerClassName='leading-7'
-                        />
-                      )
-                    })}
-                  </FormattedText>
-                </ChunkContainer>
+            {!isSetting
+              ? (
+                <div className='flex items-center mt-8 py-2'>
+                  <Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.previousStep')}</Button>
+                  <div className={s.divider} />
+                  <Button loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
+                </div>
               )
-            })
-          )}
-          {currentEstimateMutation.isIdle && (
-            <div className='h-full w-full flex items-center justify-center'>
-              <div className='flex flex-col items-center justify-center gap-3'>
-                <RiSearchEyeLine className='size-10 text-text-empty-state-icon' />
-                <p className='text-sm text-text-tertiary'>
-                  {t('datasetCreation.stepTwo.previewChunkTip')}
-                </p>
+              : (
+                <div className='flex items-center mt-8 py-2'>
+                  <Button loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.save')}</Button>
+                  <Button className='ml-2' onClick={onCancel}>{t('datasetCreation.stepTwo.cancel')}</Button>
+                </div>
+              )}
+          </div>
+        </div>
+      </div>
+      <FloatRightContainer isMobile={isMobile} isOpen={showPreview} onClose={hidePreview} footer={null}>
+        {showPreview && <div ref={previewScrollRef} className={cn(s.previewWrap, isMobile && s.isMobile, 'relative h-full overflow-y-scroll border-l border-[#F2F4F7]')}>
+          <div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
+            <div className='flex items-center justify-between px-8'>
+              <div className='grow flex items-center'>
+                <div>{t('datasetCreation.stepTwo.previewTitle')}</div>
+                {docForm === DocForm.QA && !previewSwitched && (
+                  <Button className='ml-2' variant='secondary-accent' onClick={() => previewSwitch()}>{t('datasetCreation.stepTwo.previewButton')}</Button>
+                )}
+              </div>
+              <div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
+                <XMarkIcon className='h-4 w-4'></XMarkIcon>
               </div>
             </div>
-          )}
-          {currentEstimateMutation.isPending && (
-            <div className='space-y-6'>
-              {Array.from({ length: 10 }, (_, i) => (
-                <SkeletonContainer key={i}>
-                  <SkeletonRow>
-                    <SkeletonRectangle className="w-20" />
-                    <SkeletonPoint />
-                    <SkeletonRectangle className="w-24" />
-                  </SkeletonRow>
-                  <SkeletonRectangle className="w-full" />
-                  <SkeletonRectangle className="w-full" />
-                  <SkeletonRectangle className="w-[422px]" />
-                </SkeletonContainer>
-              ))}
+            {docForm === DocForm.QA && !previewSwitched && (
+              <div className='px-8 pr-12 text-xs text-gray-500'>
+                <span>{t('datasetCreation.stepTwo.previewSwitchTipStart')}</span>
+                <span className='text-amber-600'>{t('datasetCreation.stepTwo.previewSwitchTipEnd')}</span>
+              </div>
+            )}
+          </div>
+          <div className='my-4 px-8 space-y-4'>
+            {previewSwitched && docForm === DocForm.QA && fileIndexingEstimate?.qa_preview && (
+              <>
+                {fileIndexingEstimate?.qa_preview.map((item, index) => (
+                  <PreviewItem type={PreviewType.QA} key={item.question} qa={item} index={index + 1} />
+                ))}
+              </>
+            )}
+            {(docForm === DocForm.TEXT || !previewSwitched) && fileIndexingEstimate?.preview && (
+              <>
+                {fileIndexingEstimate?.preview.map((item, index) => (
+                  <PreviewItem type={PreviewType.TEXT} key={item} content={item} index={index + 1} />
+                ))}
+              </>
+            )}
+            {previewSwitched && docForm === DocForm.QA && !fileIndexingEstimate?.qa_preview && (
+              <div className='flex items-center justify-center h-[200px]'>
+                <Loading type='area' />
+              </div>
+            )}
+            {!previewSwitched && !fileIndexingEstimate?.preview && (
+              <div className='flex items-center justify-center h-[200px]'>
+                <Loading type='area' />
+              </div>
+            )}
+          </div>
+        </div>}
+        {!showPreview && (
+          <div className={cn(s.sideTip)}>
+            <div className={s.tipCard}>
+              <span className={s.icon} />
+              <div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
+              <div className={s.content}>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
+                <p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
+              </div>
             </div>
-          )}
-        </PreviewContainer>
+          </div>
+        )}
       </FloatRightContainer>
     </div>
   )

+ 0 - 77
web/app/components/datasets/create/step-two/inputs.tsx

@@ -1,77 +0,0 @@
-import type { FC, PropsWithChildren, ReactNode } from 'react'
-import { useTranslation } from 'react-i18next'
-import type { InputProps } from '@/app/components/base/input'
-import Input from '@/app/components/base/input'
-import Tooltip from '@/app/components/base/tooltip'
-import type { InputNumberProps } from '@/app/components/base/input-number'
-import { InputNumber } from '@/app/components/base/input-number'
-
-const TextLabel: FC<PropsWithChildren> = (props) => {
-  return <label className='text-text-secondary text-xs font-semibold leading-none'>{props.children}</label>
-}
-
-const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
-  return <div className='space-y-2 flex-1'>
-    <TextLabel>{props.label}</TextLabel>
-    {props.children}
-  </div>
-}
-
-export const DelimiterInput: FC<InputProps & { tooltip?: string }> = (props) => {
-  const { t } = useTranslation()
-  return <FormField label={<div className='flex items-center mb-1'>
-    <span className='system-sm-semibold mr-0.5'>{t('datasetCreation.stepTwo.separator')}</span>
-    <Tooltip
-      popupContent={
-        <div className='max-w-[200px]'>
-          {props.tooltip || t('datasetCreation.stepTwo.separatorTip')}
-        </div>
-      }
-    />
-  </div>}>
-    <Input
-      type="text"
-      className='h-9'
-      placeholder={t('datasetCreation.stepTwo.separatorPlaceholder')!}
-      {...props}
-    />
-  </FormField>
-}
-
-export const MaxLengthInput: FC<InputNumberProps> = (props) => {
-  const { t } = useTranslation()
-  return <FormField label={<div className='system-sm-semibold mb-1'>
-    {t('datasetCreation.stepTwo.maxLength')}
-  </div>}>
-    <InputNumber
-      type="number"
-      className='h-9'
-      placeholder={'≤ 4000'}
-      max={4000}
-      min={1}
-      {...props}
-    />
-  </FormField>
-}
-
-export const OverlapInput: FC<InputNumberProps> = (props) => {
-  const { t } = useTranslation()
-  return <FormField label={<div className='flex items-center mb-1'>
-    <span className='system-sm-semibold'>{t('datasetCreation.stepTwo.overlap')}</span>
-    <Tooltip
-      popupContent={
-        <div className='max-w-[200px]'>
-          {t('datasetCreation.stepTwo.overlapTip')}
-        </div>
-      }
-    />
-  </div>}>
-    <InputNumber
-      type="number"
-      className='h-9'
-      placeholder={t('datasetCreation.stepTwo.overlap') || ''}
-      min={1}
-      {...props}
-    />
-  </FormField>
-}

+ 9 - 24
web/app/components/datasets/create/step-two/language-select/index.tsx

@@ -1,7 +1,7 @@
 'use client'
 import type { FC } from 'react'
 import React from 'react'
-import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react'
+import { RiArrowDownSLine } from '@remixicon/react'
 import cn from '@/utils/classnames'
 import Popover from '@/app/components/base/popover'
 import { languages } from '@/i18n/language'
@@ -22,40 +22,25 @@ const LanguageSelect: FC<ILanguageSelectProps> = ({
       manualClose
       trigger='click'
       disabled={disabled}
-      popupClassName='z-20'
       htmlContent={
-        <div className='w-full p-1'>
+        <div className='w-full py-1'>
           {languages.filter(language => language.supported).map(({ prompt_name }) => (
             <div
               key={prompt_name}
-              className='w-full py-2 px-3 inline-flex items-center justify-between hover:bg-state-base-hover rounded-lg cursor-pointer'
-              onClick={() => onSelect(prompt_name)}
-            >
-              <span className='text-text-secondary system-sm-medium'>{prompt_name}</span>
-              {(currentLanguage === prompt_name) && <RiCheckLine className='size-4 text-text-accent' />}
+              className='py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer text-gray-700 text-sm'
+              onClick={() => onSelect(prompt_name)}>{prompt_name}
             </div>
           ))}
         </div>
       }
       btnElement={
-        <div className={cn('inline-flex items-center gap-x-[1px]', disabled && 'cursor-not-allowed')}>
-          <span className={cn(
-            'px-[3px] system-xs-semibold text-components-button-tertiary-text',
-            disabled ? 'text-components-button-tertiary-text-disabled' : '',
-          )}>
-            {currentLanguage}
-          </span>
-          <RiArrowDownSLine className={cn(
-            'size-3.5 text-components-button-tertiary-text',
-            disabled ? 'text-components-button-tertiary-text-disabled' : '',
-          )} />
+        <div className='inline-flex items-center'>
+          <span className='pr-[2px] text-xs leading-[18px] font-medium'>{currentLanguage}</span>
+          <RiArrowDownSLine className='w-3 h-3 opacity-60' />
         </div>
       }
-      btnClassName={() => cn(
-        '!border-0 rounded-md !px-1.5 !py-1 !mx-1 !bg-components-button-tertiary-bg !hover:bg-components-button-tertiary-bg',
-        disabled ? 'bg-components-button-tertiary-bg-disabled' : '',
-      )}
-      className='!w-[140px] h-fit !z-20 !translate-x-0 !left-1'
+      btnClassName={open => cn('!border-0 !px-0 !py-0 !bg-inherit !hover:bg-inherit', open ? 'text-blue-600' : 'text-gray-500')}
+      className='!w-[120px] h-fit !z-20 !translate-x-0 !left-[-16px]'
     />
   )
 }

+ 0 - 98
web/app/components/datasets/create/step-two/option-card.tsx

@@ -1,98 +0,0 @@
-import { type ComponentProps, type FC, type ReactNode, forwardRef } from 'react'
-import Image from 'next/image'
-import classNames from '@/utils/classnames'
-
-const TriangleArrow: FC<ComponentProps<'svg'>> = props => (
-  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="11" viewBox="0 0 24 11" fill="none" {...props}>
-    <path d="M9.87868 1.12132C11.0503 -0.0502525 12.9497 -0.0502525 14.1213 1.12132L23.3137 10.3137H0.686292L9.87868 1.12132Z" fill="currentColor"/>
-  </svg>
-)
-
-type OptionCardHeaderProps = {
-  icon: ReactNode
-  title: ReactNode
-  description: string
-  isActive?: boolean
-  activeClassName?: string
-  effectImg?: string
-}
-
-export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
-  const { icon, title, description, isActive, activeClassName, effectImg } = props
-  return <div className={classNames(
-    'flex h-full overflow-hidden rounded-t-xl relative',
-    isActive && activeClassName,
-  )}>
-    <div className='size-14 flex items-center justify-center relative overflow-hidden'>
-      {isActive && effectImg && <Image src={effectImg} className='absolute top-0 left-0 w-full h-full' alt='' width={56} height={56} />}
-      <div className='p-1'>
-        <div className='size-8 rounded-lg border p-1.5 shadow-md border-components-panel-border-subtle justify-center flex bg-background-default-dodge'>
-          {icon}
-        </div>
-      </div>
-    </div>
-    <TriangleArrow
-      className='absolute left-4 -bottom-1.5 text-components-panel-bg'
-    />
-    <div className='flex-1 space-y-0.5 py-3 pr-4'>
-      <div className='text-text-secondary system-md-semibold'>{title}</div>
-      <div className='text-text-tertiary system-xs-regular'>{description}</div>
-    </div>
-  </div>
-}
-
-type OptionCardProps = {
-  icon: ReactNode
-  className?: string
-  activeHeaderClassName?: string
-  title: ReactNode
-  description: string
-  isActive?: boolean
-  actions?: ReactNode
-  effectImg?: string
-  onSwitched?: () => void
-  noHighlight?: boolean
-  disabled?: boolean
-} & Omit<ComponentProps<'div'>, 'title' | 'onClick'>
-
-export const OptionCard: FC<OptionCardProps> = forwardRef((props, ref) => {
-  const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, effectImg, onSwitched, noHighlight, disabled, ...rest } = props
-  return <div
-    className={classNames(
-      'rounded-xl bg-components-option-card-option-bg shadow-xs',
-      (isActive && !noHighlight)
-        ? 'border-[1.5px] border-components-option-card-option-selected-border'
-        : 'border border-components-option-card-option-border',
-      disabled && 'opacity-50',
-      className,
-    )}
-    style={{
-      ...style,
-    }}
-    onClick={() => {
-      if (!isActive && !disabled)
-        onSwitched?.()
-    }}
-    {...rest}
-    ref={ref}
-  >
-    <OptionCardHeader
-      icon={icon}
-      title={title}
-      description={description}
-      isActive={isActive && !noHighlight}
-      activeClassName={activeHeaderClassName}
-      effectImg={effectImg}
-    />
-    {/** Body */}
-    {isActive && (children || actions) && <div className='py-3 px-4 bg-components-panel-bg rounded-b-xl'>
-      {children}
-      {actions && <div className='flex gap-2 mt-4'>
-        {actions}
-      </div>
-      }
-    </div>}
-  </div>
-})
-
-OptionCard.displayName = 'OptionCard'

+ 0 - 27
web/app/components/datasets/create/stepper/index.tsx

@@ -1,27 +0,0 @@
-import { type FC, Fragment } from 'react'
-import type { Step } from './step'
-import { StepperStep } from './step'
-
-export type StepperProps = {
-  steps: Step[]
-  activeIndex: number
-}
-
-export const Stepper: FC<StepperProps> = (props) => {
-  const { steps, activeIndex } = props
-  return <div className='flex items-center gap-3'>
-    {steps.map((step, index) => {
-      const isLast = index === steps.length - 1
-      return (
-        <Fragment key={index}>
-          <StepperStep
-            {...step}
-            activeIndex={activeIndex}
-            index={index}
-          />
-          {!isLast && <div className='w-4 h-px bg-divider-deep' />}
-        </Fragment>
-      )
-    })}
-  </div>
-}

+ 0 - 46
web/app/components/datasets/create/stepper/step.tsx

@@ -1,46 +0,0 @@
-import type { FC } from 'react'
-import classNames from '@/utils/classnames'
-
-export type Step = {
-  name: string
-}
-
-export type StepperStepProps = Step & {
-  index: number
-  activeIndex: number
-}
-
-export const StepperStep: FC<StepperStepProps> = (props) => {
-  const { name, activeIndex, index } = props
-  const isActive = index === activeIndex
-  const isDisabled = activeIndex < index
-  const label = isActive ? `STEP ${index + 1}` : `${index + 1}`
-  return <div className='flex items-center gap-2'>
-    <div className={classNames(
-      'h-5 px-2 py-1 rounded-3xl flex-col justify-center items-center gap-2 inline-flex',
-      isActive
-        ? 'bg-state-accent-solid'
-        : !isDisabled
-          ? 'border border-text-quaternary'
-          : 'border border-divider-deep',
-    )}>
-      <div className={classNames(
-        'text-center system-2xs-semibold-uppercase',
-        isActive
-          ? 'text-text-primary-on-surface'
-          : !isDisabled
-            ? 'text-text-tertiary'
-            : 'text-text-quaternary',
-      )}>
-        {label}
-      </div>
-    </div>
-    <div className={classNames('system-xs-medium-uppercase',
-      isActive
-        ? 'text-text-accent system-xs-semibold-uppercase'
-        : !isDisabled
-          ? 'text-text-tertiary'
-          : 'text-text-quaternary',
-    )}>{name}</div>
-  </div>
-}

+ 0 - 41
web/app/components/datasets/create/top-bar/index.tsx

@@ -1,41 +0,0 @@
-import type { FC } from 'react'
-import { RiArrowLeftLine } from '@remixicon/react'
-import Link from 'next/link'
-import { useTranslation } from 'react-i18next'
-import { Stepper, type StepperProps } from '../stepper'
-import classNames from '@/utils/classnames'
-
-export type TopbarProps = Pick<StepperProps, 'activeIndex'> & {
-  className?: string
-}
-
-const STEP_T_MAP: Record<number, string> = {
-  1: 'datasetCreation.steps.one',
-  2: 'datasetCreation.steps.two',
-  3: 'datasetCreation.steps.three',
-}
-
-export const Topbar: FC<TopbarProps> = (props) => {
-  const { className, ...rest } = props
-  const { t } = useTranslation()
-  return <div className={classNames('flex shrink-0 h-[52px] items-center justify-between relative border-b border-b-divider-subtle', className)}>
-    <Link href={'/datasets'} className="h-12 pl-2 pr-6 py-2 justify-start items-center gap-1 inline-flex">
-      <div className='p-2'>
-        <RiArrowLeftLine className='size-4 text-text-primary' />
-      </div>
-      <p className="text-text-primary system-sm-semibold-uppercase">
-        {t('datasetCreation.steps.header.creation')}
-      </p>
-    </Link>
-    <div className={
-      'top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 absolute'
-    }>
-      <Stepper
-        steps={Array.from({ length: 3 }, (_, i) => ({
-          name: t(STEP_T_MAP[i + 1]),
-        }))}
-        {...rest}
-      />
-    </div>
-  </div>
-}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini