Fix: Bug fixes (#11960)

### What problem does this PR solve?

Fix: Bug fixes

New search popup style modification
Fixed multilingual settings not updating immediately on personal center
page
Changed overlapped percent to percentage format, with maximum value of
30%
### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-12-16 09:44:06 +08:00
committed by GitHub
parent 7ca3e11566
commit a98887d4ca
19 changed files with 192 additions and 137 deletions

View File

@ -11,6 +11,7 @@ import {
DefaultValues, DefaultValues,
FieldValues, FieldValues,
SubmitHandler, SubmitHandler,
UseFormTrigger,
useForm, useForm,
useFormContext, useFormContext,
} from 'react-hook-form'; } from 'react-hook-form';
@ -99,8 +100,9 @@ interface DynamicFormProps<T extends FieldValues> {
// Form ref interface // Form ref interface
export interface DynamicFormRef { export interface DynamicFormRef {
submit: () => void; submit: () => void;
getValues: () => any; getValues: (name?: string) => any;
reset: (values?: any) => void; reset: (values?: any) => void;
trigger: UseFormTrigger<any>;
watch: (field: string, callback: (value: any) => void) => () => void; watch: (field: string, callback: (value: any) => void) => () => void;
updateFieldType: (fieldName: string, newType: FormFieldType) => void; updateFieldType: (fieldName: string, newType: FormFieldType) => void;
onFieldUpdate: ( onFieldUpdate: (
@ -704,8 +706,8 @@ const DynamicForm = {
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
submit: () => form.handleSubmit(onSubmit)(), submit: form.handleSubmit,
getValues: () => form.getValues(), getValues: form.getValues,
reset: (values?: T) => { reset: (values?: T) => {
if (values) { if (values) {
form.reset(values); form.reset(values);

View File

@ -26,6 +26,7 @@ type SliderInputFormFieldProps = {
defaultValue?: number; defaultValue?: number;
className?: string; className?: string;
numberInputClassName?: string; numberInputClassName?: string;
percentage?: boolean;
} & FormLayoutType; } & FormLayoutType;
export function SliderInputFormField({ export function SliderInputFormField({
@ -39,11 +40,14 @@ export function SliderInputFormField({
className, className,
numberInputClassName, numberInputClassName,
layout = FormLayout.Horizontal, layout = FormLayout.Horizontal,
percentage = false,
}: SliderInputFormFieldProps) { }: SliderInputFormFieldProps) {
const form = useFormContext(); const form = useFormContext();
const isHorizontal = useMemo(() => layout !== FormLayout.Vertical, [layout]); const isHorizontal = useMemo(() => layout !== FormLayout.Vertical, [layout]);
const displayMax = percentage ? (max || 1) * 100 : max;
const displayMin = percentage ? (min || 0) * 100 : min;
const displayStep = percentage ? (step || 0.01) * 100 : step;
return ( return (
<FormField <FormField
control={form.control} control={form.control}
@ -71,12 +75,13 @@ export function SliderInputFormField({
<FormControl> <FormControl>
<SingleFormSlider <SingleFormSlider
{...field} {...field}
max={max} value={percentage ? field.value * 100 : field.value}
min={min} onChange={(value) =>
step={step} field.onChange(percentage ? value / 100 : value)
// defaultValue={ }
// typeof defaultValue === 'number' ? [defaultValue] : undefined max={displayMax}
// } min={displayMin}
step={displayStep}
></SingleFormSlider> ></SingleFormSlider>
</FormControl> </FormControl>
<FormControl> <FormControl>
@ -86,11 +91,20 @@ export function SliderInputFormField({
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
numberInputClassName, numberInputClassName,
)} )}
max={max} max={displayMax}
min={min} min={displayMin}
step={step} step={displayStep}
{...field} value={
// defaultValue={defaultValue} percentage ? (field.value * 100).toFixed(0) : field.value
}
onChange={(val) => {
const value = Number(val || 0);
if (!isNaN(value)) {
field.onChange(
percentage ? (value / 100).toFixed(0) : value,
);
}
}}
></NumberInput> ></NumberInput>
</FormControl> </FormControl>
</div> </div>

View File

@ -174,20 +174,23 @@ const Modal: ModalType = ({
onClick={() => maskClosable && onOpenChange?.(false)} onClick={() => maskClosable && onOpenChange?.(false)}
> >
<DialogPrimitive.Content <DialogPrimitive.Content
className={`relative w-[700px] ${full ? 'max-w-full' : sizeClasses[size]} ${className} bg-bg-base rounded-lg shadow-lg border border-border-default transition-all focus-visible:!outline-none`} className={cn(
`relative w-[700px] ${full ? 'max-w-full' : sizeClasses[size]} ${className} bg-bg-base rounded-lg shadow-lg border border-border-default transition-all focus-visible:!outline-none`,
{ 'pt-10': closable && !title },
)}
style={style} style={style}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{/* title */} {/* title */}
{(title || closable) && ( {title && (
<div <div
className={cn( className={cn(
'flex items-start px-6 py-4', 'flex items-start px-6 py-4 justify-start',
{ // {
'justify-end': closable && !title, // 'justify-end': closable && !title,
'justify-between': closable && title, // 'justify-between': closable && title,
'justify-start': !closable, // 'justify-start': !closable,
}, // },
titleClassName, titleClassName,
)} )}
> >
@ -196,19 +199,19 @@ const Modal: ModalType = ({
{title} {title}
</DialogPrimitive.Title> </DialogPrimitive.Title>
)} )}
{closable && (
<DialogPrimitive.Close asChild>
<button
type="button"
className="flex h-7 w-7 items-center justify-center text-text-secondary rounded-full hover:text-text-primary focus-visible:outline-none"
onClick={handleCancel}
>
{closeIcon}
</button>
</DialogPrimitive.Close>
)}
</div> </div>
)} )}
{closable && (
<DialogPrimitive.Close asChild>
<button
type="button"
className="flex absolute right-5 top-5 h-7 w-7 items-center justify-center text-text-secondary rounded-full hover:text-text-primary focus-visible:outline-none"
onClick={handleCancel}
>
{closeIcon}
</button>
</DialogPrimitive.Close>
)}
{/* content */} {/* content */}
<div className="py-2 px-6 overflow-y-auto scrollbar-auto max-h-[calc(100vh-280px)] focus-visible:!outline-none"> <div className="py-2 px-6 overflow-y-auto scrollbar-auto max-h-[calc(100vh-280px)] focus-visible:!outline-none">

View File

@ -159,6 +159,7 @@ export default {
doc: 'Docs', doc: 'Docs',
searchKnowledgePlaceholder: 'Search', searchKnowledgePlaceholder: 'Search',
noMoreData: `That's all. Nothing more.`, noMoreData: `That's all. Nothing more.`,
parserRequired: 'Chunk method is required',
}, },
knowledgeDetails: { knowledgeDetails: {
localUpload: 'Local upload', localUpload: 'Local upload',
@ -329,7 +330,7 @@ export default {
reRankModelWaring: 'Re-rank model is very time consuming.', reRankModelWaring: 'Re-rank model is very time consuming.',
}, },
knowledgeConfiguration: { knowledgeConfiguration: {
overlappedPercent: 'Overlapped percent', overlappedPercent: 'Overlapped percent(%)',
generationScopeTip: generationScopeTip:
'Determines whether RAPTOR is generated for the entire dataset or for a single file.', 'Determines whether RAPTOR is generated for the entire dataset or for a single file.',
scopeDataset: 'Dataset', scopeDataset: 'Dataset',

View File

@ -101,6 +101,7 @@ export default {
doc: '文档', doc: '文档',
searchKnowledgePlaceholder: '搜索', searchKnowledgePlaceholder: '搜索',
noMoreData: '没有更多数据了', noMoreData: '没有更多数据了',
parserRequired: '分块方法必填',
}, },
knowledgeDetails: { knowledgeDetails: {
localUpload: '本地上传', localUpload: '本地上传',

View File

@ -24,7 +24,8 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant'; import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant';
import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface'; import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant'; import { useDataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { IDataSourceInfoMap } from '@/pages/user-setting/data-source/interface';
import { formatDate, formatSecondsToHumanReadable } from '@/utils/date'; import { formatDate, formatSecondsToHumanReadable } from '@/utils/date';
import { import {
ColumnDef, ColumnDef,
@ -54,6 +55,7 @@ export const getFileLogsTableColumns = (
navigateToDataflowResult: ( navigateToDataflowResult: (
props: NavigateToDataflowResultProps, props: NavigateToDataflowResultProps,
) => () => void, ) => () => void,
dataSourceInfo: IDataSourceInfoMap,
) => { ) => {
// const { t } = useTranslate('knowledgeDetails'); // const { t } = useTranslate('knowledgeDetails');
const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [ const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [
@ -117,8 +119,8 @@ export const getFileLogsTableColumns = (
) : ( ) : (
<div className="w-6 h-6 flex items-center justify-center"> <div className="w-6 h-6 flex items-center justify-center">
{ {
DataSourceInfo[ dataSourceInfo[
row.original.source_from as keyof typeof DataSourceInfo row.original.source_from as keyof typeof dataSourceInfo
].icon ].icon
} }
</div> </div>
@ -368,7 +370,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
setLogInfo(logDetail); setLogInfo(logDetail);
setIsModalVisible(true); setIsModalVisible(true);
}; };
const { dataSourceInfo } = useDataSourceInfo();
const columns = useMemo(() => { const columns = useMemo(() => {
return active === LogTabs.FILE_LOGS return active === LogTabs.FILE_LOGS
? getFileLogsTableColumns( ? getFileLogsTableColumns(
@ -376,6 +378,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
showLog, showLog,
kowledgeId || '', kowledgeId || '',
navigateToDataflowResult, navigateToDataflowResult,
dataSourceInfo,
) )
: getDatasetLogsTableColumns(t, showLog); : getDatasetLogsTableColumns(t, showLog);
}, [active, t]); }, [active, t]);

View File

@ -9,7 +9,7 @@ import {
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IConnector } from '@/interfaces/database/knowledge'; import { IConnector } from '@/interfaces/database/knowledge';
import { delSourceModal } from '@/pages/user-setting/data-source/component/delete-source-modal'; import { delSourceModal } from '@/pages/user-setting/data-source/component/delete-source-modal';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant'; import { useDataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { useDataSourceRebuild } from '@/pages/user-setting/data-source/hooks'; import { useDataSourceRebuild } from '@/pages/user-setting/data-source/hooks';
import { IDataSourceBase } from '@/pages/user-setting/data-source/interface'; import { IDataSourceBase } from '@/pages/user-setting/data-source/interface';
import { Link, Settings, Unlink } from 'lucide-react'; import { Link, Settings, Unlink } from 'lucide-react';
@ -41,6 +41,7 @@ interface DataSourceItemProps extends IDataSourceNodeProps {
} }
const DataSourceItem = (props: DataSourceItemProps) => { const DataSourceItem = (props: DataSourceItemProps) => {
const { dataSourceInfo } = useDataSourceInfo();
const { t } = useTranslation(); const { t } = useTranslation();
const { id, name, icon, source, auto_parse, unbindFunc, handleAutoParse } = const { id, name, icon, source, auto_parse, unbindFunc, handleAutoParse } =
props; props;
@ -56,7 +57,7 @@ const DataSourceItem = (props: DataSourceItemProps) => {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-6 h-6 flex-shrink-0">{icon}</div> <div className="w-6 h-6 flex-shrink-0">{icon}</div>
<div className="text-base text-text-primary"> <div className="text-base text-text-primary">
{DataSourceInfo[source].name} {dataSourceInfo[source].name}
</div> </div>
<div>{name}</div> <div>{name}</div>
</div> </div>
@ -114,6 +115,7 @@ const DataSourceItem = (props: DataSourceItemProps) => {
delSourceModal({ delSourceModal({
data: props, data: props,
type: 'unlink', type: 'unlink',
dataSourceInfo: dataSourceInfo,
onOk: (data) => unbindFunc?.(data as DataSourceItemProps), onOk: (data) => unbindFunc?.(data as DataSourceItemProps),
}); });
}} }}
@ -134,6 +136,7 @@ const LinkDataSource = (props: ILinkDataSourceProps) => {
handleAutoParse, handleAutoParse,
} = props; } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const { dataSourceInfo } = useDataSourceInfo();
const [openLinkModal, setOpenLinkModal] = useState(false); const [openLinkModal, setOpenLinkModal] = useState(false);
const pipelineNode: IDataSourceNodeProps[] = useMemo(() => { const pipelineNode: IDataSourceNodeProps[] = useMemo(() => {
@ -144,7 +147,7 @@ const LinkDataSource = (props: ILinkDataSourceProps) => {
id: item?.id, id: item?.id,
name: item?.name, name: item?.name,
icon: icon:
DataSourceInfo[item?.source as keyof typeof DataSourceInfo]?.icon || dataSourceInfo[item?.source as keyof typeof dataSourceInfo]?.icon ||
'', '',
} as IDataSourceNodeProps; } as IDataSourceNodeProps;
}); });

View File

@ -291,9 +291,10 @@ export function EnableTocToggle() {
export function OverlappedPercent() { export function OverlappedPercent() {
return ( return (
<SliderInputFormField <SliderInputFormField
percentage={true}
name="parser_config.overlapped_percent" name="parser_config.overlapped_percent"
label={t('knowledgeConfiguration.overlappedPercent')} label={t('knowledgeConfiguration.overlappedPercent')}
max={0.5} max={0.3}
step={0.01} step={0.01}
></SliderInputFormField> ></SliderInputFormField>
); );

View File

@ -8,7 +8,7 @@ import { FormLayout } from '@/constants/form';
import { DocumentParserType } from '@/constants/knowledge'; import { DocumentParserType } from '@/constants/knowledge';
import { PermissionRole } from '@/constants/permission'; import { PermissionRole } from '@/constants/permission';
import { IConnector } from '@/interfaces/database/knowledge'; import { IConnector } from '@/interfaces/database/knowledge';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant'; import { useDataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { IDataSourceBase } from '@/pages/user-setting/data-source/interface'; import { IDataSourceBase } from '@/pages/user-setting/data-source/interface';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@ -89,6 +89,7 @@ export default function DatasetSettings() {
connectors: [], connectors: [],
}, },
}); });
const { dataSourceInfo } = useDataSourceInfo();
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form); const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
// const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>(); // const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>();
const [sourceData, setSourceData] = useState<IDataSourceNodeProps[]>(); const [sourceData, setSourceData] = useState<IDataSourceNodeProps[]>();
@ -113,7 +114,7 @@ export default function DatasetSettings() {
return { return {
...connector, ...connector,
icon: icon:
DataSourceInfo[connector.source as keyof typeof DataSourceInfo] dataSourceInfo[connector.source as keyof typeof dataSourceInfo]
?.icon || '', ?.icon || '',
}; };
}); });
@ -159,7 +160,7 @@ export default function DatasetSettings() {
...connector, ...connector,
auto_parse: connector.auto_parse === '0' ? '0' : '1', auto_parse: connector.auto_parse === '0' ? '0' : '1',
icon: icon:
DataSourceInfo[connector.source as keyof typeof DataSourceInfo] dataSourceInfo[connector.source as keyof typeof dataSourceInfo]
?.icon || '', ?.icon || '',
}; };
}); });

View File

@ -11,7 +11,7 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useSetDocumentStatus } from '@/hooks/use-document-request'; import { useSetDocumentStatus } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document'; import { IDocumentInfo } from '@/interfaces/database/document';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant'; import { useDataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import { ColumnDef } from '@tanstack/table-core'; import { ColumnDef } from '@tanstack/table-core';
import { ArrowUpDown, MonitorUp } from 'lucide-react'; import { ArrowUpDown, MonitorUp } from 'lucide-react';
@ -35,7 +35,7 @@ export function useDatasetTableColumns({
const { t } = useTranslation('translation', { const { t } = useTranslation('translation', {
keyPrefix: 'knowledgeDetails', keyPrefix: 'knowledgeDetails',
}); });
const { dataSourceInfo } = useDataSourceInfo();
const { navigateToChunkParsedResult } = useNavigatePage(); const { navigateToChunkParsedResult } = useNavigatePage();
const { setDocumentStatus } = useSetDocumentStatus(); const { setDocumentStatus } = useSetDocumentStatus();
@ -134,8 +134,8 @@ export function useDatasetTableColumns({
) : ( ) : (
<div className="w-6 h-6 flex items-center justify-center"> <div className="w-6 h-6 flex items-center justify-center">
{ {
DataSourceInfo[ dataSourceInfo[
row.original.source_type as keyof typeof DataSourceInfo row.original.source_type as keyof typeof dataSourceInfo
]?.icon ]?.icon
} }
</div> </div>

View File

@ -1,7 +1,6 @@
import { CardContainer } from '@/components/card-container'; import { CardContainer } from '@/components/card-container';
import { EmptyCardType } from '@/components/empty/constant'; import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty'; import { EmptyAppCard } from '@/components/empty/empty';
import { IconFont } from '@/components/icon-font';
import ListFilterBar from '@/components/list-filter-bar'; import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog'; import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -149,7 +148,7 @@ export default function SearchList() {
onOk={onSearchRenameConfirm} onOk={onSearchRenameConfirm}
initialName={initialSearchName} initialName={initialSearchName}
loading={searchRenameLoading} loading={searchRenameLoading}
title={<IconFont name="search" className="size-6"></IconFont>} title={initialSearchName || t('createSearch')}
></RenameDialog> ></RenameDialog>
)} )}
</section> </section>

View File

@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { Settings, Trash2 } from 'lucide-react'; import { Settings, Trash2 } from 'lucide-react';
import { useDataSourceInfo } from '../contant';
import { useDeleteDataSource } from '../hooks'; import { useDeleteDataSource } from '../hooks';
import { IDataSorceInfo, IDataSourceBase } from '../interface'; import { IDataSorceInfo, IDataSourceBase } from '../interface';
import { delSourceModal } from './delete-source-modal'; import { delSourceModal } from './delete-source-modal';
@ -13,6 +14,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
const { list, name, icon } = props; const { list, name, icon } = props;
const { handleDelete } = useDeleteDataSource(); const { handleDelete } = useDeleteDataSource();
const { navigateToDataSourceDetail } = useNavigatePage(); const { navigateToDataSourceDetail } = useNavigatePage();
const { dataSourceInfo } = useDataSourceInfo();
const toDetail = (id: string) => { const toDetail = (id: string) => {
navigateToDataSourceDetail(id); navigateToDataSourceDetail(id);
}; };
@ -49,6 +51,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
onClick={() => onClick={() =>
delSourceModal({ delSourceModal({
data: item, data: item,
dataSourceInfo: dataSourceInfo,
onOk: () => { onOk: () => {
handleDelete(item); handleDelete(item);
}, },

View File

@ -1,20 +1,19 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Modal, ModalType } from '@/components/ui/modal/modal'; import { Modal, ModalType } from '@/components/ui/modal/modal';
import { t } from 'i18next'; import { t } from 'i18next';
import { DataSourceInfo } from '../contant'; import { IDataSourceBase, IDataSourceInfoMap } from '../interface';
import { IDataSourceBase } from '../interface';
export type IDelSourceModalProps<T> = Partial<ModalType> & { export type IDelSourceModalProps<T> = Partial<ModalType> & {
data?: T; data?: T;
type?: 'delete' | 'unlink'; type?: 'delete' | 'unlink';
onOk?: (data?: T) => void; onOk?: (data?: T) => void;
dataSourceInfo: IDataSourceInfoMap;
}; };
export const delSourceModal = <T extends IDataSourceBase>( export const delSourceModal = <T extends IDataSourceBase>(
props: IDelSourceModalProps<T>, props: IDelSourceModalProps<T>,
) => { ) => {
const { data, onOk, type = 'delete', ...otherProps } = props; const { data, onOk, type = 'delete', dataSourceInfo, ...otherProps } = props;
console.log('data', data);
const config = { const config = {
title: title:
type === 'delete' type === 'delete'
@ -39,7 +38,7 @@ export const delSourceModal = <T extends IDataSourceBase>(
)} )}
<div className="flex items-center gap-1 p-2 border border-border-button rounded-md mb-3"> <div className="flex items-center gap-1 p-2 border border-border-button rounded-md mb-3">
<div className="w-6 h-6 flex-shrink-0"> <div className="w-6 h-6 flex-shrink-0">
{data?.source ? DataSourceInfo[data?.source].icon : ''} {data?.source ? dataSourceInfo[data?.source].icon : ''}
</div> </div>
<div className="flex items-center gap-2 text-text-secondary text-xs"> <div className="flex items-center gap-2 text-text-secondary text-xs">
{/* <div className="h-6 flex-shrink-0 text-text-primary text-base"> {/* <div className="h-6 flex-shrink-0 text-text-primary text-base">

View File

@ -1,11 +1,13 @@
import { FormFieldType } from '@/components/dynamic-form'; import { FormFieldType } from '@/components/dynamic-form';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { t } from 'i18next'; import { t, TFunction } from 'i18next';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import BoxTokenField from './component/box-token-field'; import BoxTokenField from './component/box-token-field';
import { ConfluenceIndexingModeField } from './component/confluence-token-field'; import { ConfluenceIndexingModeField } from './component/confluence-token-field';
import GmailTokenField from './component/gmail-token-field'; import GmailTokenField from './component/gmail-token-field';
import GoogleDriveTokenField from './component/google-drive-token-field'; import GoogleDriveTokenField from './component/google-drive-token-field';
import { IDataSourceInfoMap } from './interface';
export enum DataSourceKey { export enum DataSourceKey {
CONFLUENCE = 'confluence', CONFLUENCE = 'confluence',
S3 = 's3', S3 = 's3',
@ -23,62 +25,75 @@ export enum DataSourceKey {
// TEAMS = 'teams', // TEAMS = 'teams',
} }
export const DataSourceInfo = { export const generateDataSourceInfo = (t: TFunction) => {
[DataSourceKey.S3]: { return {
name: 'S3', [DataSourceKey.S3]: {
description: t(`setting.${DataSourceKey.S3}Description`), name: 'S3',
icon: <SvgIcon name={'data-source/s3'} width={38} />, description: t(`setting.${DataSourceKey.S3}Description`),
}, icon: <SvgIcon name={'data-source/s3'} width={38} />,
[DataSourceKey.NOTION]: { },
name: 'Notion', [DataSourceKey.NOTION]: {
description: t(`setting.${DataSourceKey.NOTION}Description`), name: 'Notion',
icon: <SvgIcon name={'data-source/notion'} width={38} />, description: t(`setting.${DataSourceKey.NOTION}Description`),
}, icon: <SvgIcon name={'data-source/notion'} width={38} />,
[DataSourceKey.DISCORD]: { },
name: 'Discord', [DataSourceKey.DISCORD]: {
description: t(`setting.${DataSourceKey.DISCORD}Description`), name: 'Discord',
icon: <SvgIcon name={'data-source/discord'} width={38} />, description: t(`setting.${DataSourceKey.DISCORD}Description`),
}, icon: <SvgIcon name={'data-source/discord'} width={38} />,
[DataSourceKey.CONFLUENCE]: { },
name: 'Confluence', [DataSourceKey.CONFLUENCE]: {
description: t(`setting.${DataSourceKey.CONFLUENCE}Description`), name: 'Confluence',
icon: <SvgIcon name={'data-source/confluence'} width={38} />, description: t(`setting.${DataSourceKey.CONFLUENCE}Description`),
}, icon: <SvgIcon name={'data-source/confluence'} width={38} />,
[DataSourceKey.GOOGLE_DRIVE]: { },
name: 'Google Drive', [DataSourceKey.GOOGLE_DRIVE]: {
description: t(`setting.${DataSourceKey.GOOGLE_DRIVE}Description`), name: 'Google Drive',
icon: <SvgIcon name={'data-source/google-drive'} width={38} />, description: t(`setting.${DataSourceKey.GOOGLE_DRIVE}Description`),
}, icon: <SvgIcon name={'data-source/google-drive'} width={38} />,
[DataSourceKey.GMAIL]: { },
name: 'Gmail', [DataSourceKey.GMAIL]: {
description: t(`setting.${DataSourceKey.GMAIL}Description`), name: 'Gmail',
icon: <SvgIcon name={'data-source/gmail'} width={38} />, description: t(`setting.${DataSourceKey.GMAIL}Description`),
}, icon: <SvgIcon name={'data-source/gmail'} width={38} />,
[DataSourceKey.MOODLE]: { },
name: 'Moodle', [DataSourceKey.MOODLE]: {
description: t(`setting.${DataSourceKey.MOODLE}Description`), name: 'Moodle',
icon: <SvgIcon name={'data-source/moodle'} width={38} />, description: t(`setting.${DataSourceKey.MOODLE}Description`),
}, icon: <SvgIcon name={'data-source/moodle'} width={38} />,
[DataSourceKey.JIRA]: { },
name: 'Jira', [DataSourceKey.JIRA]: {
description: t(`setting.${DataSourceKey.JIRA}Description`), name: 'Jira',
icon: <SvgIcon name={'data-source/jira'} width={38} />, description: t(`setting.${DataSourceKey.JIRA}Description`),
}, icon: <SvgIcon name={'data-source/jira'} width={38} />,
[DataSourceKey.WEBDAV]: { },
name: 'WebDAV', [DataSourceKey.WEBDAV]: {
description: t(`setting.${DataSourceKey.WEBDAV}Description`), name: 'WebDAV',
icon: <SvgIcon name={'data-source/webdav'} width={38} />, description: t(`setting.${DataSourceKey.WEBDAV}Description`),
}, icon: <SvgIcon name={'data-source/webdav'} width={38} />,
[DataSourceKey.DROPBOX]: { },
name: 'Dropbox', [DataSourceKey.DROPBOX]: {
description: t(`setting.${DataSourceKey.DROPBOX}Description`), name: 'Dropbox',
icon: <SvgIcon name={'data-source/dropbox'} width={38} />, description: t(`setting.${DataSourceKey.DROPBOX}Description`),
}, icon: <SvgIcon name={'data-source/dropbox'} width={38} />,
[DataSourceKey.BOX]: { },
name: 'Box', [DataSourceKey.BOX]: {
description: t(`setting.${DataSourceKey.BOX}Description`), name: 'Box',
icon: <SvgIcon name={'data-source/box'} width={38} />, description: t(`setting.${DataSourceKey.BOX}Description`),
}, icon: <SvgIcon name={'data-source/box'} width={38} />,
},
};
};
export const useDataSourceInfo = () => {
const { t } = useTranslation();
const [dataSourceInfo, setDataSourceInfo] = useState<IDataSourceInfoMap>(
generateDataSourceInfo(t) as IDataSourceInfoMap,
);
useEffect(() => {
setDataSourceInfo(generateDataSourceInfo(t));
}, [t]);
return { dataSourceInfo };
}; };
export const DataSourceFormBaseFields = [ export const DataSourceFormBaseFields = [

View File

@ -18,7 +18,7 @@ import {
DataSourceFormBaseFields, DataSourceFormBaseFields,
DataSourceFormDefaultValues, DataSourceFormDefaultValues,
DataSourceFormFields, DataSourceFormFields,
DataSourceInfo, useDataSourceInfo,
} from '../contant'; } from '../contant';
import { import {
useAddDataSource, useAddDataSource,
@ -32,10 +32,10 @@ const SourceDetailPage = () => {
const { data: detail } = useFetchDataSourceDetail(); const { data: detail } = useFetchDataSourceDetail();
const { handleResume } = useDataSourceResume(); const { handleResume } = useDataSourceResume();
const { dataSourceInfo } = useDataSourceInfo();
const detailInfo = useMemo(() => { const detailInfo = useMemo(() => {
if (detail) { if (detail) {
return DataSourceInfo[detail.source]; return dataSourceInfo[detail.source];
} }
}, [detail]); }, [detail]);

View File

@ -12,10 +12,11 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
import { t } from 'i18next'; import { t } from 'i18next';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useParams, useSearchParams } from 'umi'; import { useParams, useSearchParams } from 'umi';
import { DataSourceInfo, DataSourceKey } from './contant'; import { DataSourceKey, useDataSourceInfo } from './contant';
import { IDataSorceInfo, IDataSource, IDataSourceBase } from './interface'; import { IDataSorceInfo, IDataSource, IDataSourceBase } from './interface';
export const useListDataSource = () => { export const useListDataSource = () => {
const { dataSourceInfo } = useDataSourceInfo();
const { data: list, isFetching } = useQuery<IDataSource[]>({ const { data: list, isFetching } = useQuery<IDataSource[]>({
queryKey: ['data-source'], queryKey: ['data-source'],
queryFn: async () => { queryFn: async () => {
@ -49,12 +50,12 @@ export const useListDataSource = () => {
[]; [];
Object.keys(categorizedData).forEach((key: string) => { Object.keys(categorizedData).forEach((key: string) => {
const k = key as DataSourceKey; const k = key as DataSourceKey;
if (DataSourceInfo[k]) { if (dataSourceInfo[k]) {
sourceList.push({ sourceList.push({
id: k, id: k,
name: DataSourceInfo[k].name, name: dataSourceInfo[k].name,
description: DataSourceInfo[k].description, description: dataSourceInfo[k].description,
icon: DataSourceInfo[k].icon, icon: dataSourceInfo[k].icon,
list: categorizedData[k] || [], list: categorizedData[k] || [],
}); });
} }

View File

@ -10,21 +10,21 @@ import {
} from '../components/user-setting-header'; } from '../components/user-setting-header';
import AddDataSourceModal from './add-datasource-modal'; import AddDataSourceModal from './add-datasource-modal';
import { AddedSourceCard } from './component/added-source-card'; import { AddedSourceCard } from './component/added-source-card';
import { DataSourceInfo, DataSourceKey } from './contant'; import { DataSourceKey, useDataSourceInfo } from './contant';
import { useAddDataSource, useListDataSource } from './hooks'; import { useAddDataSource, useListDataSource } from './hooks';
import { IDataSorceInfo } from './interface'; import { IDataSorceInfo } from './interface';
const dataSourceTemplates = Object.values(DataSourceKey).map((id) => {
return {
id,
name: DataSourceInfo[id].name,
description: DataSourceInfo[id].description,
icon: DataSourceInfo[id].icon,
};
});
const DataSource = () => { const DataSource = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { dataSourceInfo } = useDataSourceInfo();
const dataSourceTemplates = Object.values(DataSourceKey).map((id) => {
return {
id,
name: dataSourceInfo[id].name,
description: dataSourceInfo[id].description,
icon: dataSourceInfo[id].icon,
};
});
// useListTenantUser(); // useListTenantUser();
const { categorizedList } = useListDataSource(); const { categorizedList } = useListDataSource();

View File

@ -43,3 +43,11 @@ export interface IDataSourceLog {
tenant_id: string; tenant_id: string;
timeout_secs: number; timeout_secs: number;
} }
interface IDataSourceInfoItem {
name: string;
description: string;
icon: JSX.Element;
}
export type IDataSourceInfoMap = Record<DataSourceKey, IDataSourceInfoItem>;

View File

@ -11,12 +11,13 @@ import {
} from '@/hooks/use-user-setting-request'; } from '@/hooks/use-user-setting-request';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { t } from 'i18next'; import { TFunction } from 'i18next';
import { Banknote, Box, Server, Unplug, User, Users } from 'lucide-react'; import { Banknote, Box, Server, Unplug, User, Users } from 'lucide-react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useHandleMenuClick } from './hooks'; import { useHandleMenuClick } from './hooks';
const menuItems = [ const menuItems = (t: TFunction) => [
{ icon: Server, label: t('setting.dataSources'), key: Routes.DataSource }, { icon: Server, label: t('setting.dataSources'), key: Routes.DataSource },
{ icon: Box, label: t('setting.model'), key: Routes.Model }, { icon: Box, label: t('setting.model'), key: Routes.Model },
{ icon: Banknote, label: 'MCP', key: Routes.Mcp }, { icon: Banknote, label: 'MCP', key: Routes.Mcp },
@ -32,12 +33,12 @@ const menuItems = [
// { icon: Cog, label: t('setting.system'), key: Routes.System }, // { icon: Cog, label: t('setting.system'), key: Routes.System },
// { icon: Banknote, label: 'Plan', key: Routes.Plan }, // { icon: Banknote, label: 'Plan', key: Routes.Plan },
]; ];
export function SideBar() { export function SideBar() {
const pathName = useSecondPathName(); const pathName = useSecondPathName();
const { data: userInfo } = useFetchUserInfo(); const { data: userInfo } = useFetchUserInfo();
const { handleMenuClick, active } = useHandleMenuClick(); const { handleMenuClick, active } = useHandleMenuClick();
const { version, fetchSystemVersion } = useFetchSystemVersion(); const { version, fetchSystemVersion } = useFetchSystemVersion();
const { t } = useTranslation();
useEffect(() => { useEffect(() => {
if (location.host !== Domain) { if (location.host !== Domain) {
fetchSystemVersion(); fetchSystemVersion();
@ -56,7 +57,7 @@ export function SideBar() {
<p className="text-sm text-text-primary">{userInfo?.email}</p> <p className="text-sm text-text-primary">{userInfo?.email}</p>
</div> </div>
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
{menuItems.map((item, idx) => { {menuItems(t).map((item, idx) => {
const hoverKey = pathName === item.key; const hoverKey = pathName === item.key;
return ( return (
<div key={idx}> <div key={idx}>