Fix: Improve some functional issues with the data source. #10703 (#11081)

### What problem does this PR solve?

Fix: Improve some functional issues with the data source. #10703

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-11-06 20:07:38 +08:00
committed by GitHub
parent 42edecc98f
commit 0b7b88592f
19 changed files with 937 additions and 79 deletions

View File

@ -49,7 +49,7 @@ export interface IFileLogItem {
process_duration: number;
progress: number;
progress_msg: string;
source_from: string;
source_type: string;
status: string;
task_type: string;
tenant_id: string;

View File

@ -11,11 +11,18 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { RunningStatusMap } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { cn } from '@/lib/utils';
import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant';
import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { formatDate, formatSecondsToHumanReadable } from '@/utils/date';
import {
ColumnDef,
@ -77,25 +84,34 @@ export const getFileLogsTableColumns = (
{
accessorKey: 'fileName',
header: t('fileName'),
meta: { cellClassName: 'max-w-[20vw]' },
cell: ({ row }) => (
<div
className="flex items-center gap-2 text-text-primary"
// onClick={navigateToDataflowResult(
// row.original.id,
// row.original.kb_id,
// )}
>
<FileIcon name={row.original.document_name}></FileIcon>
{row.original.document_name}
</div>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex gap-2 cursor-pointer">
<FileIcon name={row.original.document_name}></FileIcon>
<span className={cn('truncate')}>
{row.original.document_name}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
<p>{row.original.document_name}</p>
</TooltipContent>
</Tooltip>
),
},
{
accessorKey: 'source_from',
header: t('source'),
meta: { cellClassName: 'max-w-[10vw]' },
cell: ({ row }) => (
<div className="text-text-primary">
{row.original.source_from || t('localUpload')}
{row.original.source_type
? DataSourceInfo[
row.original.source_type as keyof typeof DataSourceInfo
].icon
: t('localUpload')}
</div>
),
},

View File

@ -66,7 +66,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
<div
key={item.id}
className={cn(
'flex flex-row items-center justify-between rounded-md bg-bg-input px-2 py-1 cursor-pointer',
'flex flex-row items-center justify-between rounded-md bg-bg-card px-2 py-1 cursor-pointer',
// { hidden: item.name.indexOf(filterString) <= -1 },
)}
onClick={() => {

View File

@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button';
import { Modal } from '@/components/ui/modal/modal';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IConnector } from '@/interfaces/database/knowledge';
import { delSourceModal } from '@/pages/user-setting/data-source/component/delete-source-modal';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { IDataSourceBase } from '@/pages/user-setting/data-source/interface';
import { Link, Settings, Unlink } from 'lucide-react';
@ -26,7 +27,7 @@ interface DataSourceItemProps extends IDataSourceNodeProps {
const DataSourceItem = (props: DataSourceItemProps) => {
const { t } = useTranslation();
const { id, name, icon, openLinkModalFunc, unbindFunc } = props;
const { id, name, icon, unbindFunc } = props;
const { navigateToDataSourceDetail } = useNavigatePage();
const toDetail = (id: string) => {
@ -71,7 +72,7 @@ const DataSourceItem = (props: DataSourceItemProps) => {
return (
<div className="flex items-center justify-between gap-1 px-2 rounded-md border ">
<div className="flex items-center gap-1">
{icon}
<div className="w-6 h-6 flex-shrink-0">{icon}</div>
<div>{name}</div>
</div>
<div className="flex gap-1 items-center">
@ -94,7 +95,12 @@ const DataSourceItem = (props: DataSourceItemProps) => {
variant={'transparent'}
className="border-none"
onClick={() => {
openUnlinkModal();
// openUnlinkModal();
delSourceModal({
data: props,
type: 'unlink',
onOk: (data) => unbindFunc?.(data as DataSourceItemProps),
});
}}
>
<Unlink />

View File

@ -61,6 +61,7 @@ export const useFetchKnowledgeConfigurationOnMount = (
'parser_id',
'language',
'parser_config',
'connectors',
'pagerank',
'avatar',
]),

View File

@ -248,7 +248,7 @@ export default function DatasetSettings() {
/>
)}
<Divider />
{/* <Divider /> */}
{parseType === 1 && <ChunkMethodForm />}
{/* <LinkDataPipeline

View File

@ -11,6 +11,7 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useSetDocumentStatus } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document';
import { cn } from '@/lib/utils';
import { DataSourceInfo } from '@/pages/user-setting/data-source/contant';
import { formatDate } from '@/utils/date';
import { ColumnDef } from '@tanstack/table-core';
import { ArrowUpDown } from 'lucide-react';
@ -120,6 +121,19 @@ export function useDatasetTableColumns({
</div>
),
},
{
accessorKey: 'source_from',
header: t('source'),
cell: ({ row }) => (
<div className="text-text-primary">
{row.original.source_type
? DataSourceInfo[
row.original.source_type as keyof typeof DataSourceInfo
]?.icon || t('localUpload')
: t('localUpload')}
</div>
),
},
{
accessorKey: 'status',
header: t('enabled'),

View File

@ -1,9 +1,9 @@
import { DynamicForm, FormFieldConfig } from '@/components/dynamic-form';
import { Modal } from '@/components/ui/modal/modal';
import { IModalProps } from '@/interfaces/common';
import { useEffect, useState } from 'react';
import { FieldValues } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { DynamicForm, FormFieldConfig } from './component/dynamic-form';
import {
DataSourceFormBaseFields,
DataSourceFormDefaultValues,
@ -39,7 +39,12 @@ const AddDataSourceModal = ({
return (
<Modal
title={t('setting.add')}
title={
<div className="flex flex-col">
{sourceData?.icon}
{t('setting.addDataSourceModalTital', { name: sourceData?.name })}
</div>
}
open={visible || false}
onOpenChange={(open) => !open && hideModal?.()}
// onOk={() => handleOk()}

View File

@ -1,9 +1,9 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { Settings, Trash2 } from 'lucide-react';
import { useDeleteDataSource } from '../hooks';
import { IDataSorceInfo, IDataSourceBase } from '../interface';
import { delSourceModal } from './delete-source-modal';
export type IAddedSourceCardProps = IDataSorceInfo & {
list: IDataSourceBase[];
@ -19,7 +19,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
<Card className="bg-transparent border border-border-button px-5 pt-[10px] pb-5 rounded-md">
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-0 pb-3">
{/* <Users className="mr-2 h-5 w-5 text-[#1677ff]" /> */}
<CardTitle className="text-base flex gap-1 font-normal">
<CardTitle className="text-base items-center flex gap-1 font-normal">
{icon}
{name}
</CardTitle>
@ -28,7 +28,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
{list.map((item) => (
<div
key={item.id}
className="flex flex-row items-center justify-between rounded-md bg-bg-input px-[10px] py-4"
className="flex flex-row items-center justify-between rounded-md bg-bg-card px-[10px] py-4"
>
<div className="text-sm text-text-secondary ">{item.name}</div>
<div className="text-sm text-text-secondary flex gap-2">
@ -39,9 +39,20 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
toDetail(item.id);
}}
/>
<ConfirmDeleteDialog onOk={() => handleDelete(item)}>
<Trash2 className="cursor-pointer" size={14} />
</ConfirmDeleteDialog>
{/* <ConfirmDeleteDialog onOk={() => handleDelete(item)}> */}
<Trash2
className="cursor-pointer"
size={14}
onClick={() =>
delSourceModal({
data: item,
onOk: () => {
handleDelete(item);
},
})
}
/>
{/* </ConfirmDeleteDialog> */}
</div>
</div>
))}

View File

@ -0,0 +1,80 @@
import { Button } from '@/components/ui/button';
import { Modal, ModalType } from '@/components/ui/modal/modal';
import { t } from 'i18next';
import { DataSourceInfo } from '../contant';
import { IDataSourceBase } from '../interface';
export type IDelSourceModalProps<T> = Partial<ModalType> & {
data?: T;
type?: 'delete' | 'unlink';
onOk?: (data?: T) => void;
};
export const delSourceModal = <T extends IDataSourceBase>(
props: IDelSourceModalProps<T>,
) => {
const { data, onOk, type = 'delete', ...otherProps } = props;
console.log('data', data);
const config = {
title:
type === 'delete'
? t('setting.deleteSourceModalTitle')
: t('dataflowParser.unlinkSourceModalTitle'),
content: (
<div className="px-2 py-6">
<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">
{data?.source ? DataSourceInfo[data?.source].icon : ''}
</div>
<div>{data?.name}</div>
</div>
{type === 'delete' ? (
<div
className="text-sm text-text-secondary"
dangerouslySetInnerHTML={{
__html: t('setting.deleteSourceModalContent'),
}}
></div>
) : (
<div
className="text-sm text-text-secondary"
dangerouslySetInnerHTML={{
__html: t('dataflowParser.unlinkSourceModalContent'),
}}
/>
)}
</div>
),
confirmText:
type === 'delete'
? t('setting.deleteSourceModalConfirmText')
: t('dataflowParser.unlinkSourceModalConfirmText'),
};
Modal.show({
visible: true,
className: '!w-[560px]',
...otherProps,
title: config.title,
children: config.content,
onVisibleChange: () => {
Modal.hide();
},
footer: (
<div className="flex justify-end gap-2">
<Button variant={'outline'} onClick={() => Modal.hide()}>
{t('dataflowParser.changeStepModalCancelText')}
</Button>
<Button
variant={'secondary'}
className="!bg-state-error text-text-base"
onClick={() => {
onOk?.(data);
Modal.hide();
}}
>
{config.confirmText}
</Button>
</div>
),
});
};

View File

@ -1,12 +1,12 @@
import { FormFieldType } from '@/components/dynamic-form';
import SvgIcon from '@/components/svg-icon';
import { t } from 'i18next';
import { FormFieldType } from './component/dynamic-form';
export enum DataSourceKey {
CONFLUENCE = 'confluence',
S3 = 's3',
NOTION = 'notion',
DISCORD = 'discord',
// CONFLUENNCE = 'confluence',
// GMAIL = 'gmail',
// GOOGLE_DRIVER = 'google_driver',
// JIRA = 'jira',
@ -19,17 +19,22 @@ export const DataSourceInfo = {
[DataSourceKey.S3]: {
name: 'S3',
description: t(`setting.${DataSourceKey.S3}Description`),
icon: <SvgIcon name={'data-source/s3'} width={28} />,
icon: <SvgIcon name={'data-source/s3'} width={38} />,
},
[DataSourceKey.NOTION]: {
name: 'Notion',
description: t(`setting.${DataSourceKey.NOTION}Description`),
icon: <SvgIcon name={'data-source/notion'} width={28} />,
icon: <SvgIcon name={'data-source/notion'} width={38} />,
},
[DataSourceKey.DISCORD]: {
name: 'Discord',
description: t(`setting.${DataSourceKey.DISCORD}Description`),
icon: <SvgIcon name={'data-source/discord'} width={28} />,
icon: <SvgIcon name={'data-source/discord'} width={38} />,
},
[DataSourceKey.CONFLUENCE]: {
name: 'Confluence',
description: t(`setting.${DataSourceKey.CONFLUENCE}Description`),
icon: <SvgIcon name={'data-source/confluence'} width={38} />,
},
};
@ -71,7 +76,7 @@ export const DataSourceFormFields = {
{
label: 'AWS Secret Access Key',
name: 'config.credentials.aws_secret_access_key',
type: FormFieldType.Text,
type: FormFieldType.Password,
required: true,
},
{
@ -103,7 +108,7 @@ export const DataSourceFormFields = {
{
label: 'Notion Integration Token',
name: 'config.credentials.notion_integration_token',
type: FormFieldType.Text,
type: FormFieldType.Password,
required: true,
},
{
@ -117,7 +122,7 @@ export const DataSourceFormFields = {
{
label: 'Discord Bot Token',
name: 'config.credentials.discord_bot_token',
type: FormFieldType.Text,
type: FormFieldType.Password,
required: true,
},
{
@ -133,6 +138,38 @@ export const DataSourceFormFields = {
required: false,
},
],
[DataSourceKey.CONFLUENCE]: [
{
label: 'Confluence Username',
name: 'config.credentials.confluence_username',
type: FormFieldType.Text,
required: true,
tooltip: 'A descriptive name for the connector.',
},
{
label: 'Confluence Access Token',
name: 'config.credentials.confluence_access_token',
type: FormFieldType.Password,
required: true,
},
{
label: 'Wiki Base URL',
name: 'config.wiki_base',
type: FormFieldType.Text,
required: false,
tooltip:
'The base URL of your Confluence instance (e.g., https://your-domain.atlassian.net/wiki)',
},
{
label: 'Is Cloud',
name: 'config.is_cloud',
type: FormFieldType.Checkbox,
required: false,
tooltip:
'Check if this is a Confluence Cloud instance, uncheck for Confluence Server/Data Center',
},
],
};
export const DataSourceFormDefaultValues = {
@ -170,4 +207,16 @@ export const DataSourceFormDefaultValues = {
},
},
},
[DataSourceKey.CONFLUENCE]: {
name: '',
source: DataSourceKey.CONFLUENCE,
config: {
wiki_base: '',
is_cloud: true,
credentials: {
confluence_username: '',
confluence_access_token: '',
},
},
},
};

View File

@ -1,4 +1,10 @@
import BackButton from '@/components/back-button';
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} from '@/components/dynamic-form';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
@ -8,12 +14,6 @@ import { debounce } from 'lodash';
import { CirclePause, Repeat } from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FieldValues } from 'react-hook-form';
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} from '../component/dynamic-form';
import {
DataSourceFormBaseFields,
DataSourceFormDefaultValues,
@ -112,7 +112,7 @@ const SourceDetailPage = () => {
<div className="flex items-center gap-1 w-full relative">
<Input {...fieldProps} type={FormFieldType.Number} />
<span className="absolute right-0 -translate-x-3 text-text-secondary italic ">
minutes
seconds
</span>
</div>
),

View File

@ -145,7 +145,7 @@ export const DataSourceLogsTable = () => {
const handleToDataSetDetail = useCallback(
(id: string) => {
console.log('handleToDataSetDetail', id);
navigate(`${Routes.DatasetBase}${Routes.DataSetSetting}/${id}`);
navigate(`${Routes.DatasetBase}${Routes.DatasetBase}/${id}`);
},
[navigate],
);

View File

@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { CardTitle } from '@/components/ui/card';
import { useTranslation } from 'react-i18next';
import Spotlight from '@/components/spotlight';
@ -11,21 +11,17 @@ import { DataSourceInfo, DataSourceKey } from './contant';
import { useAddDataSource, useListDataSource } from './hooks';
import { IDataSorceInfo } from './interface';
const dataSourceTemplates = [
{
id: DataSourceKey.CONFLUENCE,
name: DataSourceInfo[DataSourceKey.CONFLUENCE].name,
description: DataSourceInfo[DataSourceKey.CONFLUENCE].description,
icon: DataSourceInfo[DataSourceKey.CONFLUENCE].icon,
},
{
id: DataSourceKey.S3,
name: DataSourceInfo[DataSourceKey.S3].name,
description: DataSourceInfo[DataSourceKey.S3].description,
icon: DataSourceInfo[DataSourceKey.S3].icon,
list: [
{
id: '1',
name: 'S3 Bucket 1',
},
{
id: '2',
name: 'S3 Bucket 1',
},
],
},
{
id: DataSourceKey.DISCORD,
@ -94,16 +90,16 @@ const DataSource = () => {
<div className="w-full flex flex-col gap-4 relative ">
<Spotlight />
<Card className="bg-transparent border-none px-0">
<CardHeader className="flex flex-row items-center justify-between space-y-0 px-4 pt-4 pb-0">
<CardTitle className="text-2xl font-medium">
{t('setting.dataSources')}
<div className="text-sm text-text-secondary">
{t('setting.datasourceDescription')}
</div>
</CardTitle>
</CardHeader>
</Card>
{/* <Card className="bg-transparent border-none px-0"> */}
<section className="flex flex-row items-center justify-between space-y-0 px-4 pt-4 pb-0">
<div className="text-2xl font-medium">
{t('setting.dataSources')}
<div className="text-sm text-text-secondary">
{t('setting.datasourceDescription')}
</div>
</div>
</section>
{/* </Card> */}
<Separator className="border-border-button bg-border-button " />
<div className=" flex flex-col gap-4 p-4 max-h-[calc(100vh-120px)] overflow-y-auto overflow-x-hidden scrollbar-auto">
<div className="flex flex-col gap-3">
@ -111,8 +107,8 @@ const DataSource = () => {
<AddedSourceCard key={index} {...item} />
))}
</div>
<Card className="bg-transparent border-none mt-8">
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-0 pb-4">
<section className="bg-transparent border-none mt-8">
<header className="flex flex-row items-center justify-between space-y-0 p-0 pb-4">
{/* <Users className="mr-2 h-5 w-5 text-[#1677ff]" /> */}
<CardTitle className="text-2xl font-semibold">
{t('setting.availableSources')}
@ -120,16 +116,16 @@ const DataSource = () => {
{t('setting.availableSourcesDescription')}
</div>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
</header>
<main className="p-0">
{/* <TenantTable searchTerm={searchTerm}></TenantTable> */}
<div className="grid sm:grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-4 3xl:grid-cols-4 gap-4">
{dataSourceTemplates.map((item, index) => (
<AbailableSourceCard {...item} key={index} />
))}
</div>
</CardContent>
</Card>
</main>
</section>
</div>
{addingModalVisible && (