type check

This commit is contained in:
Stephen Zhou 2025-12-29 11:31:58 +08:00
parent 74107a8069
commit de2a4fe3a5
No known key found for this signature in database
13 changed files with 40 additions and 25 deletions

View File

@ -66,7 +66,7 @@ describe('UpgradeBtn', () => {
it('should render custom label when labelKey is provided', () => {
// Act
render(<UpgradeBtn labelKey="custom.label.key" />)
render(<UpgradeBtn labelKey={'custom.label.key' as any} />)
// Assert
expect(screen.getByText(/custom\.label\.key/i)).toBeInTheDocument()
@ -74,7 +74,7 @@ describe('UpgradeBtn', () => {
it('should render custom label in plain button when labelKey is provided with isPlain', () => {
// Act
render(<UpgradeBtn isPlain labelKey="custom.label.key" />)
render(<UpgradeBtn isPlain labelKey={'custom.label.key' as any} />)
// Assert
const button = screen.getByRole('button')
@ -372,7 +372,7 @@ describe('UpgradeBtn', () => {
it('should handle empty string labelKey', () => {
// Act
render(<UpgradeBtn labelKey="" />)
render(<UpgradeBtn labelKey={'' as any} />)
// Assert - empty labelKey is falsy, so it falls back to default label
expect(screen.getByText(/billing\.upgradeBtn\.encourage/i)).toBeInTheDocument()
@ -391,7 +391,7 @@ describe('UpgradeBtn', () => {
it('should handle isPlain with custom labelKey', () => {
// Act
render(<UpgradeBtn isPlain labelKey="custom.key" />)
render(<UpgradeBtn isPlain labelKey={'custom.key' as any} />)
// Assert - labelKey should override plain text
expect(screen.getByText(/custom\.key/i)).toBeInTheDocument()
@ -400,7 +400,7 @@ describe('UpgradeBtn', () => {
it('should handle isShort with custom labelKey', () => {
// Act
render(<UpgradeBtn isShort labelKey="custom.short.key" />)
render(<UpgradeBtn isShort labelKey={'custom.short.key' as any} />)
// Assert - labelKey should override isShort behavior
expect(screen.getByText(/custom\.short\.key/i)).toBeInTheDocument()
@ -423,7 +423,7 @@ describe('UpgradeBtn', () => {
isShort
onClick={handleClick}
loc="test-loc"
labelKey="custom.all"
labelKey={'custom.all' as any}
/>,
)
const badge = screen.getByText(/custom\.all/i)

View File

@ -17,7 +17,7 @@ type Props = {
isShort?: boolean
onClick?: () => void
loc?: string
labelKey?: I18nKeysWithPrefix<'billing', 'upgradeBtn.'>
labelKey?: Exclude<I18nKeysWithPrefix<'billing'>, 'plans.community.features' | 'plans.enterprise.features' | 'plans.premium.features'>
}
const UpgradeBtn: FC<Props> = ({

View File

@ -1,6 +1,7 @@
'use client'
import type { NotionPage } from '@/models/common'
import type { CrawlOptions, CrawlResultItem, createDocumentResponse, FileItem } from '@/models/datasets'
import type { RETRIEVE_METHOD } from '@/types/app'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback, useState } from 'react'
@ -43,7 +44,7 @@ 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 [retrievalMethodCache, setRetrievalMethodCache] = useState<RETRIEVE_METHOD | ''>('')
const [fileList, setFiles] = useState<FileItem[]>([])
const [result, setResult] = useState<createDocumentResponse | undefined>()
const [notionPages, setNotionPages] = useState<NotionPage[]>([])
@ -90,7 +91,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
setResult(res)
}, [])
const updateRetrievalMethodCache = useCallback((method: string) => {
const updateRetrievalMethodCache = useCallback((method: RETRIEVE_METHOD | '') => {
setRetrievalMethodCache(method)
}, [])
@ -166,7 +167,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
datasetId={datasetId}
datasetName={datasetDetail?.name}
indexingType={datasetDetail?.indexing_technique || indexingTypeCache}
retrievalMethod={datasetDetail?.retrieval_model_dict?.search_method || retrievalMethodCache}
retrievalMethod={datasetDetail?.retrieval_model_dict?.search_method || retrievalMethodCache || undefined}
creationCache={result}
/>
)}

View File

@ -1,5 +1,6 @@
import type { createDocumentResponse, FullDocumentDetail, IconInfo } from '@/models/datasets'
import { render, screen } from '@testing-library/react'
import { RETRIEVE_METHOD } from '@/types/app'
import StepThree from './index'
// Mock the EmbeddingProcess component since it has complex async logic
@ -321,7 +322,7 @@ describe('StepThree', () => {
describe('retrievalMethod prop', () => {
it('should pass retrievalMethod to EmbeddingProcess', () => {
// Arrange & Act
renderStepThree({ retrievalMethod: 'semantic_search' })
renderStepThree({ retrievalMethod: RETRIEVE_METHOD.semantic })
// Assert
expect(screen.getByTestId('ep-retrieval-method')).toHaveTextContent('semantic_search')

View File

@ -88,7 +88,7 @@ type StepTwoProps = {
websiteCrawlJobId?: string
onStepChange?: (delta: number) => void
updateIndexingTypeCache?: (type: string) => void
updateRetrievalMethodCache?: (method: string) => void
updateRetrievalMethodCache?: (method: RETRIEVE_METHOD | '') => void
updateResultCache?: (res: createDocumentResponse) => void
onSave?: () => void
onCancel?: () => void
@ -552,7 +552,7 @@ const StepTwo = ({
onSuccess(data) {
updateIndexingTypeCache?.(indexType as string)
updateResultCache?.(data)
updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
updateRetrievalMethodCache?.(retrievalConfig.search_method as RETRIEVE_METHOD)
},
},
)
@ -562,7 +562,7 @@ const StepTwo = ({
onSuccess(data) {
updateIndexingTypeCache?.(indexType as string)
updateResultCache?.(data)
updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
updateRetrievalMethodCache?.(retrievalConfig.search_method as RETRIEVE_METHOD)
},
})
}

View File

@ -100,7 +100,7 @@ const BatchAction: FC<IBatchActionProps> = ({
onClick={onBatchReIndex}
>
<RiRefreshLine className="size-4" />
<span className="px-0.5">{t(`${i18nPrefix}.reIndex`)}</span>
<span className="px-0.5">{t(`${i18nPrefix}.reIndex`, { ns: 'dataset' })}</span>
</Button>
)}
<Button

View File

@ -255,11 +255,11 @@ const DocumentList: FC<IDocumentListProps> = ({
const [e] = await asyncRunSafe<CommonResponse>(retryIndexDocument({ datasetId, documentIds: selectedIds }))
if (!e) {
onSelectedIdChange([])
Toast.notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
onUpdate()
}
else {
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) })
}
}

View File

@ -3,6 +3,7 @@
import type {
ReactNode,
} from 'react'
import type { TagKey } from '../constants'
import type { Plugin } from '../types'
import type {
MarketplaceCollection,
@ -113,7 +114,7 @@ export const MarketplaceContextProvider = ({
return data?.plugins.map(plugin => plugin.plugin_id)
}, [data?.plugins, shouldExclude])
const queryFromSearchParams = searchParams?.q || ''
const tagsFromSearchParams = searchParams?.tags ? getValidTagKeys(searchParams.tags.split(',')) : []
const tagsFromSearchParams = searchParams?.tags ? getValidTagKeys(searchParams.tags.split(',') as TagKey[]) : []
const hasValidTags = !!tagsFromSearchParams.length
const hasValidCategory = getValidCategoryKeys(searchParams?.category)
const categoryFromSearchParams = hasValidCategory || PLUGIN_TYPE_SEARCH_MAP.all

View File

@ -1,11 +1,14 @@
import { LanguagesSupported } from '@/i18n-config/language'
import type {
TagKey,
} from './constants'
import { LanguagesSupported } from '@/i18n-config/language'
import {
categoryKeys,
tagKeys,
} from './constants'
export const getValidTagKeys = (tags: string[]) => {
export const getValidTagKeys = (tags: TagKey[]) => {
return tags.filter(tag => tagKeys.includes(tag))
}

View File

@ -39,6 +39,7 @@ const Blocks = ({
type: block.type,
title: block.title,
author: 'Dify',
// @ts-expect-error Fix this missing field later
description: block.description,
},
defaultValue: {},

View File

@ -131,6 +131,7 @@ export const TONE_LIST = [
{
id: 4,
name: 'Custom',
config: undefined,
},
] as const

View File

@ -89,6 +89,7 @@
"indexingMethod.full_text_search": "FULL TEXT",
"indexingMethod.hybrid_search": "HYBRID",
"indexingMethod.invertedIndex": "INVERTED",
"indexingMethod.keyword_search": "KEYWORD",
"indexingMethod.semantic_search": "VECTOR",
"indexingTechnique.economy": "ECO",
"indexingTechnique.high_quality": "HQ",

16
web/types/i18n.d.ts vendored
View File

@ -13,13 +13,19 @@ declare module 'i18next' {
export type I18nKeysByPrefix<
NS extends NamespaceCamelCase,
Prefix extends string = '',
> = keyof Resources[NS] extends infer K
? K extends `${Prefix}${infer Rest}`
? Rest
> = Prefix extends ''
? keyof Resources[NS]
: keyof Resources[NS] extends infer K
? K extends `${Prefix}${infer Rest}`
? Rest
: never
: never
: never
export type I18nKeysWithPrefix<
NS extends NamespaceCamelCase,
Prefix extends string = '',
> = Extract<keyof Resources[NS], `${Prefix}${string}`>
> = Prefix extends ''
? keyof Resources[NS]
: Extract<keyof Resources[NS], `${Prefix}${string}`>
type A = I18nKeysWithPrefix<'billing'>