feat: most ui for create datasets

chore: upd
This commit is contained in:
AkaraChen 2024-11-20 14:55:59 +08:00
parent ca4d0fb4cc
commit 27ece2fb52
14 changed files with 237 additions and 89 deletions

View File

@ -2,11 +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 Selection from '../../create/assets/selection-mod.svg'
import { RETRIEVE_METHOD } from '@/types/app'
import type { RetrievalConfig } from '@/types/app'
import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
type Props = {
value: RetrievalConfig
@ -21,7 +22,7 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
return (
<div className='space-y-2'>
<OptionCard icon={<HighPriority className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={Selection} alt='' />}
title={t('dataset.retrieval.invertedIndex.title')}
description={t('dataset.retrieval.invertedIndex.description')} isActive>
<RetrievalParamConfig

View File

@ -2,8 +2,13 @@
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 Selection from '../../create/assets/selection-mod.svg'
import Research from '../../create/assets/research-mod.svg'
import PatternRecognition from '../../create/assets/pattern-recognition-mod.svg'
import Effect from '../../create/assets/option-card-effect-purple.svg'
import type { RetrievalConfig } from '@/types/app'
import { RETRIEVE_METHOD } from '@/types/app'
import { useProviderContext } from '@/context/provider-context'
@ -14,8 +19,6 @@ import {
RerankingModeEnum,
WeightedScoreEnum,
} from '@/models/datasets'
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
@ -56,7 +59,7 @@ const RetrievalMethodConfig: FC<Props> = ({
return (
<div className='space-y-2'>
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<OptionCard icon={<Semantic className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={Selection} alt='' />}
title={t('dataset.retrieval.semantic_search.title')}
description={t('dataset.retrieval.semantic_search.description')}
isActive={
@ -66,6 +69,8 @@ const RetrievalMethodConfig: FC<Props> = ({
...value,
search_method: RETRIEVE_METHOD.semantic,
})}
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.semantic}
@ -75,7 +80,7 @@ const RetrievalMethodConfig: FC<Props> = ({
</OptionCard>
)}
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<OptionCard icon={<FileSearch02 className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={Research} alt='' />}
title={t('dataset.retrieval.full_text_search.title')}
description={t('dataset.retrieval.full_text_search.description')}
isActive={
@ -85,6 +90,8 @@ const RetrievalMethodConfig: FC<Props> = ({
...value,
search_method: RETRIEVE_METHOD.fullText,
})}
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.fullText}
@ -94,7 +101,7 @@ const RetrievalMethodConfig: FC<Props> = ({
</OptionCard>
)}
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<OptionCard icon={<PatternRecognition className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={PatternRecognition} alt='' />}
title={
<div className='flex items-center space-x-1'>
<div>{t('dataset.retrieval.hybrid_search.title')}</div>
@ -109,6 +116,8 @@ const RetrievalMethodConfig: FC<Props> = ({
search_method: RETRIEVE_METHOD.hybrid,
reranking_enable: true,
})}
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.hybrid}

View File

@ -3,6 +3,9 @@ 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'
@ -209,7 +212,11 @@ const RetrievalParamConfig: FC<Props> = ({
key={option.value}
isChosen={value.reranking_mode === option.value}
onChosen={() => handleChangeRerankMode(option.value)}
icon={<div className='w-4 h-4 text-[#7839EE]' />}
icon={<Image src={
option.value === RerankingModeEnum.WeightedScore
? ProgressIndicator
: Reranking
} alt=''/>}
title={option.label}
description={option.tips}
className='flex-1'

View File

@ -0,0 +1,5 @@
<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>

After

Width:  |  Height:  |  Size: 699 B

View File

@ -0,0 +1,5 @@
<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>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,12 @@
<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>

After

Width:  |  Height:  |  Size: 613 B

View File

@ -0,0 +1,12 @@
<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>

After

Width:  |  Height:  |  Size: 610 B

View File

@ -0,0 +1,12 @@
<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>

After

Width:  |  Height:  |  Size: 610 B

View File

@ -0,0 +1,8 @@
<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>

After

Width:  |  Height:  |  Size: 835 B

View File

@ -0,0 +1,13 @@
<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>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -117,7 +117,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
return (
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
<div className='flex flex-col' style={{ height: 'calc(100vh - 56px)' }}>
<div className="grow bg-white">
<div className={step === 1 ? 'block h-full' : 'hidden'}>
<StepOne

View File

@ -1,20 +1,25 @@
'use client'
import type { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react'
import type { FC, PropsWithChildren, ReactNode } 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 { MagnifyingGlassCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
import { XMarkIcon } from '@heroicons/react/20/solid'
import { RocketLaunchIcon } from '@heroicons/react/24/outline'
import {
RiCloseLine,
RiSearchEyeLine,
} from '@remixicon/react'
import Link from 'next/link'
import { groupBy } from 'lodash-es'
import Image from 'next/image'
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 GoldIcon from '../assets/gold.svg'
import Piggybank from '../assets/piggy-bank-mod.svg'
import Note from '../assets/note-mod.svg'
import FileList from '../assets/file-list-3-fill.svg'
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'
@ -39,11 +44,8 @@ import Toast from '@/app/components/base/toast'
import type { NotionPage } from '@/models/common'
import { DataSourceProvider } from '@/models/common'
import { DataSourceType, DocForm } from '@/models/datasets'
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'
@ -53,6 +55,18 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
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'
const TextLabel: FC<PropsWithChildren> = (props) => {
return <label className='text-[#354052] 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>
}
type ValueOf<T> = T[keyof T]
type StepTwoProps = {
@ -579,26 +593,6 @@ const StepTwo = ({
}
}, [segmentationType, indexType])
const Label: FC<PropsWithChildren> = (props) => {
return <label className='text-[#354052] text-xs font-semibold leading-none'>{props.children}</label>
}
const FormItem: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
return <div className='space-y-2 flex-1'>
<Label>{props.label}</Label>
{props.children}
</div>
}
const CheckboxWithLabel: FC<PropsWithChildren<ComponentProps<typeof Checkbox> & {
label: string
}>> = (props) => {
return <div className='flex items-center gap-2'>
<Checkbox />
<Label>{props.label}</Label>
</div>
}
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict || {
search_method: RETRIEVE_METHOD.semantic,
reranking_enable: false,
@ -637,14 +631,14 @@ const StepTwo = ({
<OptionCard
title={'General'}
icon={<Image src={SettingCog} alt='General' />}
activeHeaderClassName='bg-gradient-to-r from-blue-50/40 to-[#ffffff]'
activeHeaderClassName='bg-gradient-to-r from-[#EFF0F9] to-[#F9FAFB]'
description={'General text chunking mode, the chunks retrieved and recalled are the same.'}
isActive={SegmentType.AUTO === segmentationType}
onClick={() => setSegmentationType(SegmentType.AUTO)}
actions={
<>
<Button variant={'secondary-accent'}>
<MagnifyingGlassCircleIcon className='size-4 mr-2' />
<RiSearchEyeLine className='h-4 w-4 mr-1.5' />
Preview Chunk
</Button>
<Button variant={'ghost'} disabled>Reset</Button>
@ -653,7 +647,7 @@ const StepTwo = ({
>
<div className='space-y-4'>
<div className='flex gap-2'>
<FormItem label={<div className='flex'>
<FormField label={<div className='flex'>
{t('datasetCreation.stepTwo.separator')}
<Tooltip
popupContent={
@ -669,8 +663,8 @@ const StepTwo = ({
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier}
onChange={e => setSegmentIdentifier(e.target.value)}
/>
</FormItem>
<FormItem label={<div>
</FormField>
<FormField label={<div>
{t('datasetCreation.stepTwo.maxLength')}
</div>}>
<Input
@ -682,8 +676,8 @@ const StepTwo = ({
min={1}
onChange={e => setMax(parseInt(e.target.value.replace(/^0+/, ''), 10))}
/>
</FormItem>
<FormItem label={<div className='flex'>
</FormField>
<FormField label={<div className='flex'>
{t('datasetCreation.stepTwo.overlap')}
<Tooltip
popupContent={
@ -700,11 +694,11 @@ const StepTwo = ({
value={overlap}
min={1}
onChange={e => setOverlap(parseInt(e.target.value.replace(/^0+/, ''), 10))} />
</FormItem>
</FormField>
</div>
<div className='space-y-2'>
<div className='w-full flex flex-col'>
<Label>{t('datasetCreation.stepTwo.rules')}</Label>
<TextLabel>{t('datasetCreation.stepTwo.rules')}</TextLabel>
<div className='mt-4 space-y-2'>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem} onClick={() => {
@ -723,16 +717,76 @@ const StepTwo = ({
</OptionCard>
<OptionCard
title={'Parent-child'}
icon={undefined}
activeHeaderClassName='bg-gradient-to-r from-red-50/40 to-[#ffffff]'
icon={<Image src={FamilyMod} alt='Parent-child' />}
effectImg={OrangeEffect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F9F1EE] to-[#F9FAFB]'
description={'When using the parent-child mode, the child-chunk is used for retrieval and the parent-chunk is used for recall as context.'}
isActive={SegmentType.CUSTOM === segmentationType}
onClick={() => setSegmentationType(SegmentType.CUSTOM)}
actions={
<>
<Button variant={'secondary-accent'}>
<RiSearchEyeLine className='h-4 w-4 mr-1.5' />
Preview Chunk
</Button>
<Button variant={'ghost'} onClick={resetRules}>Reset</Button>
</>
}
>
<div className='space-y-4'>
<Label>
Parent-chunk for Context
</Label>
<TextLabel>
Parent-chunk for Context
</TextLabel>
<RadioCard
icon={<Image src={Note} alt='' />}
title={'Paragraph'}
description={'This mode splits the text in to paragraphs based on delimiters and the maximum chunk length, using the split text as the parent chunk for retrieval.'}
isChosen={true}
chosenConfig={
<div className='flex gap-2'>
<FormField label={'Delimiter'}>
<Input type="text" placeholder={'\n\n'} value={segmentIdentifier} onChange={e => setSegmentIdentifier(e.target.value)} />
</FormField>
<FormField label={'Maximum chunk length'}>
<Input type="number" placeholder={'\n\n'} value={segmentIdentifier} onChange={e => setSegmentIdentifier(e.target.value)} />
</FormField>
</div>
}
/>
<RadioCard
icon={<Image src={FileList} alt='' />}
title={'Full Doc'}
description={'The entire document is used as the parent chunk and retrieved directly. Please note that for performance reasons, text exceeding 10000 tokens will be automatically truncated.'}
isChosen={true}
/>
<TextLabel>
Child-chunk for Retrieval
</TextLabel>
<div className='flex gap-2'>
<FormField label={'Delimiter'}>
<Input type="text" placeholder={'\n'} value={segmentIdentifier} onChange={e => setSegmentIdentifier(e.target.value)} />
</FormField>
<FormField label={'Maximum chunk length'}>
<Input type="number" placeholder={'\n'} value={segmentIdentifier} onChange={e => setSegmentIdentifier(e.target.value)} />
</FormField>
</div>
<TextLabel>
Text Pre-processing Rules
</TextLabel>
<div className='space-y-2'>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem} onClick={() => {
ruleChangeHandle(rule.id)
}}>
<Checkbox
checked={rule.enabled}
/>
<label className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
</div>
))}
</div>
</div>
</OptionCard>
</div>
@ -755,7 +809,9 @@ const StepTwo = ({
setIndexType(IndexingType.QUALIFIED)
}}
>
<span className={cn(s.typeIcon, s.qualified)} />
<div className='h-8 p-1.5 bg-white rounded-lg border border-[#101828]/10 justify-center items-center inline-flex absolute left-5 top-[18px]'>
<Image src={GoldIcon} alt='Gold Icon' width={20} height={20} />
</div>
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>
@ -784,7 +840,9 @@ const StepTwo = ({
)}
onClick={changeToEconomicalType}
>
<span className={cn(s.typeIcon, s.economical)} />
<div className='h-8 p-1.5 bg-white rounded-lg border border-[#101828]/10 justify-center items-center inline-flex absolute left-5 top-[18px]'>
<Image src={Piggybank} alt='Economical Icon' width={20} height={20} />
</div>
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.economical')}</div>
@ -799,35 +857,6 @@ const StepTwo = ({
<Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
</div>
)}
{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>
)}
{/* Embedding model */}
{indexType === IndexingType.QUALIFIED && (
<div className='mb-2'>

View File

@ -1,10 +1,11 @@
import { type ComponentProps, type FC, type ReactNode } from 'react'
import Image from 'next/image'
import piggyBank from '../assets/piggy-bank-01.svg'
import Effect from '../assets/option-card-effect-blue.svg'
import classNames from '@/utils/classnames'
const TriangleArrow = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="11" viewBox="0 0 24 11" fill="none">
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="white"/>
</svg>
)
@ -15,18 +16,25 @@ type OptionCardHeaderProps = {
description: string
isActive?: boolean
activeClassName?: string
effectImg?: string
}
export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
const { icon, title, description, isActive, activeClassName } = props
const { icon, title, description, isActive, activeClassName, effectImg } = props
return <div className={classNames(
'h-20 flex py-3 px-4 items-center gap-4',
'flex h-full overflow-hidden relative',
isActive && activeClassName,
)}>
<div className='size-8 rounded-lg border p-1.5 shadow border-[#101828]/10 justify-center flex'>
{icon || <Image src={piggyBank.src} className='size-5' alt={description} width={20} height={20} />}
<div className='size-14 flex items-center justify-center relative overflow-hidden'>
{isActive && <Image src={effectImg || Effect.src} className='absolute top-0 left-0 w-full h-full' alt='' width={56} height={56} />}
<div className='size-8 rounded-lg border p-1.5 shadow border-[#101828]/10 justify-center flex bg-white'>
{icon || <Image src={piggyBank.src} className='size-5' alt={description} width={20} height={20} />}
</div>
</div>
<div className='flex-1 space-y-1'>
<TriangleArrow
className='absolute left-4 -bottom-1.5'
/>
<div className='flex-1 space-y-1 py-3'>
<div className='text-[#354052] text-sm font-semibold leading-tight'>{title}</div>
<div className='text-[#676f83] text-xs font-normal leading-none'>{description}</div>
</div>
@ -41,10 +49,11 @@ type OptionCardProps = {
description: string
isActive?: boolean
actions?: ReactNode
effectImg?: string
} & Omit<ComponentProps<'div'>, 'title'>
export const OptionCard: FC<OptionCardProps> = (props) => {
const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, ...rest } = props
const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, effectImg, ...rest } = props
return <div
className={classNames(
'rounded-xl overflow-hidden',
@ -62,6 +71,7 @@ export const OptionCard: FC<OptionCardProps> = (props) => {
description={description}
isActive={isActive}
activeClassName={activeHeaderClassName}
effectImg={effectImg}
/>
{/** Body */}
{isActive && <div className='p-3'>{children}

View File

@ -0,0 +1,25 @@
'use client'
import Input from '../components/base/input'
import { OptionCard } from '../components/datasets/create/step-two/option-card'
export default function Page() {
return <div className='p-4'>
<OptionCard
icon={undefined}
title={'General'}
description={
'General text chunking mode, the chunks retrieved and recalled are the same.'
}
className='w-[600px]'
activeHeaderClassName='bg-gradient-to-r from-[#EFF0F9] to-[#F9FAFB]'
isActive={true}>
<p
className='text-[#354052] text-sm font-semibold leading-tight'
>
Lorem ipsum
</p>
<Input className='mt-2' />
</OptionCard>
</div>
}