mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-12 20:03:40 +08:00
refactor(ui): adjust dataset page styles (#13452)
### What problem does this PR solve? - Adjust UI styles in **Dataset** pages. - Adjust several shared components styles - Modify files and directory structure in `src/layouts` ### Type of change - [x] Refactoring
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
import { useIsDarkTheme, useTheme } from '@/components/theme-provider';
|
||||
import { ThemeEnum } from '@/constants/common';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Root, Thumb } from '@radix-ui/react-switch';
|
||||
import { LucideMoon, LucideSun } from 'lucide-react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
const ThemeSwitch = forwardRef<
|
||||
React.ElementRef<typeof Root>,
|
||||
React.ComponentPropsWithoutRef<typeof Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { setTheme } = useTheme();
|
||||
const isDark = useIsDarkTheme();
|
||||
|
||||
return (
|
||||
<Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'group/theme-switch relative rounded-full outline-none self-center focus-visible:ring-1 focus-visible:ring-accent-primary',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
checked={isDark}
|
||||
onCheckedChange={(value) =>
|
||||
setTheme(value ? ThemeEnum.Dark : ThemeEnum.Light)
|
||||
}
|
||||
>
|
||||
<div className="self-center p-3 py-2 rounded-full bg-bg-card transition-[background-color]">
|
||||
<div className="h-full flex items-center justify-between gap-4 relative z-[1] text-text-disabled transition-[text-color]">
|
||||
<LucideSun
|
||||
className={cn('size-[1em]', !isDark && 'text-text-primary')}
|
||||
/>
|
||||
<LucideMoon
|
||||
className={cn('size-[1em]', isDark && 'text-text-primary')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Thumb
|
||||
className={cn(
|
||||
'absolute top-0 left-0 w-[calc(50%+.25rem)] p-0.5 h-full rounded-full overflow-hidden',
|
||||
'transition-all ease-out',
|
||||
'group-hover/theme-switch:w-[calc(50%+.66rem)] group-focus-visible/theme-switch:w-[calc(50%+.66rem)]',
|
||||
{
|
||||
'left-[calc(50%-.25rem)] group-hover/theme-switch:left-[calc(50%-.66rem)] group-focus-visible/theme-switch:left-[calc(50%-.66rem)]':
|
||||
isDark,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="size-full rounded-full bg-bg-base shadow-md transition-colors ease-out" />
|
||||
</Thumb>
|
||||
</Root>
|
||||
);
|
||||
});
|
||||
|
||||
export default ThemeSwitch;
|
||||
@@ -20,7 +20,7 @@ import { getSystemVersion, logout } from '@/services/admin-service';
|
||||
|
||||
import authorizationUtil from '@/utils/authorization-util';
|
||||
|
||||
import ThemeSwitch from '../components/theme-switch';
|
||||
import ThemeSwitch from '../../../components/theme-switch';
|
||||
import { IS_ENTERPRISE } from '../utils';
|
||||
import { CurrentUserInfoContext } from './root-layout';
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ import authorizationUtil from '@/utils/authorization-util';
|
||||
|
||||
import { login } from '@/services/admin-service';
|
||||
|
||||
import ThemeSwitch from '../../components/theme-switch';
|
||||
import { BgSvg } from '../login-next/bg';
|
||||
import ThemeSwitch from './components/theme-switch';
|
||||
|
||||
import { CurrentUserInfoContext } from './layouts/root-layout';
|
||||
|
||||
|
||||
@@ -50,25 +50,23 @@ export function DatasetActionCell({
|
||||
}, [record, showRenameModal]);
|
||||
|
||||
return (
|
||||
<section className="flex gap-4 items-center text-text-sub-title-invert opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<div
|
||||
className="
|
||||
flex gap-2 items-center opacity-0
|
||||
transition-opacity group-hover:opacity-100 group-focus-within:opacity-100"
|
||||
>
|
||||
<Button
|
||||
variant="transparent"
|
||||
className="border-none hover:bg-bg-card text-text-primary"
|
||||
size={'sm'}
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
disabled={isRunning}
|
||||
onClick={handleRename}
|
||||
>
|
||||
<PenLine />
|
||||
<PenLine className="size-[1em]" />
|
||||
</Button>
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>
|
||||
<Button
|
||||
variant="transparent"
|
||||
className="border-none hover:bg-bg-card text-text-primary"
|
||||
disabled={isRunning}
|
||||
size={'sm'}
|
||||
>
|
||||
<Eye />
|
||||
<Button size="icon-xs" variant="ghost" disabled={isRunning}>
|
||||
<Eye className="size-[1em]" />
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-[40vw] max-h-[40vh] overflow-auto">
|
||||
@@ -94,26 +92,24 @@ export function DatasetActionCell({
|
||||
|
||||
{isVirtualDocument || (
|
||||
<Button
|
||||
variant="transparent"
|
||||
className="border-none hover:bg-bg-card text-text-primary"
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
onClick={onDownloadDocument}
|
||||
disabled={isRunning}
|
||||
size={'sm'}
|
||||
>
|
||||
<Download />
|
||||
<Download className="size-[1em]" />
|
||||
</Button>
|
||||
)}
|
||||
<ConfirmDeleteDialog onOk={handleRemove}>
|
||||
<Button
|
||||
data-testid="document-delete"
|
||||
variant="transparent"
|
||||
className="border-none hover:bg-bg-card text-text-primary"
|
||||
size={'sm'}
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
disabled={isRunning}
|
||||
>
|
||||
<Trash2 />
|
||||
<Trash2 className="size-[1em]" />
|
||||
</Button>
|
||||
</ConfirmDeleteDialog>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,25 +199,27 @@ const Generate: React.FC<GenerateProps> = (props) => {
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
|
||||
<DropdownMenuTrigger asChild disabled={disabled}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
className={cn(disabled && '!cursor-not-allowed')}
|
||||
variant="transparent"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
if (!disabled) {
|
||||
handleOpenChange(!open);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<WandSparkles />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
className={cn(disabled && '!cursor-not-allowed')}
|
||||
variant="transparent"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
if (!disabled) {
|
||||
handleOpenChange(!open);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<WandSparkles />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent>{t('knowledgeDetails.generate')}</TooltipContent>
|
||||
</Tooltip>
|
||||
<TooltipContent>{t('knowledgeDetails.generate')}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[380px] p-5 flex flex-col gap-2 ">
|
||||
{Object.values(GenerateType).map((name) => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FileUploadDialog } from '@/components/file-upload-dialog';
|
||||
import ListFilterBar from '@/components/list-filter-bar';
|
||||
import { RenameDialog } from '@/components/rename-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -16,7 +17,7 @@ import {
|
||||
import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection';
|
||||
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||
import { Upload } from 'lucide-react';
|
||||
import { LucidePlus } from 'lucide-react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MetadataType } from '../components/metedata/constant';
|
||||
@@ -136,10 +137,12 @@ export default function Dataset() {
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="p-5 min-w-[880px]">
|
||||
<Card
|
||||
as="article"
|
||||
className="mb-5 mr-5 min-w-[880px] bg-transparent shadow-none"
|
||||
>
|
||||
<CardHeader as="header" className="p-5 space-y-0">
|
||||
<ListFilterBar
|
||||
title={t('header.dataset')}
|
||||
onSearchChange={handleInputChange}
|
||||
searchString={searchString}
|
||||
value={filterValue}
|
||||
@@ -147,12 +150,15 @@ export default function Dataset() {
|
||||
onChange={handleFilterSubmit}
|
||||
onOpenChange={onOpenChange}
|
||||
filters={filters}
|
||||
className="items-end"
|
||||
leftPanel={
|
||||
<div className="items-start">
|
||||
<div className="pb-1">{t('knowledgeDetails.subbarFiles')}</div>
|
||||
<div className="text-text-secondary text-sm">
|
||||
<div>
|
||||
<h1 className="leading-normal font-medium">
|
||||
{t('knowledgeDetails.subbarFiles')}
|
||||
</h1>
|
||||
<p className="text-text-secondary text-sm font-normal">
|
||||
{t('knowledgeDetails.datasetDescription')}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
preChildren={<Generate disabled={!(dataSetData.chunk_num > 0)} />}
|
||||
@@ -190,12 +196,12 @@ export default function Dataset() {
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size={'sm'}>
|
||||
<Upload />
|
||||
<Button size="default">
|
||||
<LucidePlus />
|
||||
{t('knowledgeDetails.addFile')}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuContent className="w-auto min-w-40" align="end">
|
||||
<DropdownMenuItem onClick={showDocumentUploadModal}>
|
||||
{t('fileManager.uploadFile')}
|
||||
</DropdownMenuItem>
|
||||
@@ -206,12 +212,17 @@ export default function Dataset() {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ListFilterBar>
|
||||
|
||||
{rowSelectionIsEmpty || (
|
||||
<BulkOperateBar
|
||||
className="!mt-2.5 !-mb-2.5"
|
||||
list={updatedList as BulkOperateItemType[]}
|
||||
count={selectedCount}
|
||||
></BulkOperateBar>
|
||||
/>
|
||||
)}
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="px-5 py-0">
|
||||
<DatasetTable
|
||||
documents={documents}
|
||||
pagination={pagination}
|
||||
@@ -220,7 +231,8 @@ export default function Dataset() {
|
||||
setRowSelection={setRowSelection}
|
||||
showManageMetadataModal={showManageMetadataModal}
|
||||
loading={loading}
|
||||
></DatasetTable>
|
||||
/>
|
||||
|
||||
{documentUploadVisible && (
|
||||
<FileUploadDialog
|
||||
hideModal={hideDocumentUploadModal}
|
||||
@@ -282,7 +294,7 @@ export default function Dataset() {
|
||||
hideModal={hideReparseDialogModal}
|
||||
></ReparseDialog>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,9 +84,8 @@ export const PopoverContent = ({ record }: IProps) => {
|
||||
export function ParsingCard({ record, handleShowLog }: IProps) {
|
||||
return (
|
||||
<Button
|
||||
variant={'transparent'}
|
||||
className="border-none"
|
||||
size={'sm'}
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() => handleShowLog?.(record)}
|
||||
>
|
||||
<Dot run={record.run}></Dot>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IconFontFill } from '@/components/icon-font';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -24,10 +25,10 @@ import { useHandleRunDocumentByIds } from './use-run-document';
|
||||
import { isParserRunning } from './utils';
|
||||
const IconMap = {
|
||||
[RunningStatus.UNSTART]: (
|
||||
<IconFontFill name="play" className="text-accent-primary" />
|
||||
<IconFontFill name="play" className="text-accent-primary size-[1em]" />
|
||||
),
|
||||
[RunningStatus.RUNNING]: (
|
||||
<CircleX size={14} color="rgba(var(--state-error))" />
|
||||
<CircleX color="rgba(var(--state-error))" className="size-[1em]" />
|
||||
),
|
||||
[RunningStatus.CANCEL]: (
|
||||
<IconFontFill name="reparse" className="text-accent-primary" />
|
||||
@@ -49,25 +50,63 @@ const ParseStatusStateMap = {
|
||||
[RunningStatus.SCHEDULE]: 'running',
|
||||
} as const;
|
||||
|
||||
export function ParsingStatusCell({
|
||||
export function ParseDropdownButton({
|
||||
record,
|
||||
showChangeParserModal,
|
||||
// showSetMetaModal,
|
||||
}: {
|
||||
record: IDocumentInfo;
|
||||
} & UseChangeDocumentParserShowType) {
|
||||
const { t } = useTranslation();
|
||||
const { pipeline_id, pipeline_name, parser_id } = record;
|
||||
|
||||
const handleShowChangeParserModal = useCallback(() => {
|
||||
showChangeParserModal(record);
|
||||
}, [record, showChangeParserModal]);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="static" size="auto" className="capitalize">
|
||||
{pipeline_id
|
||||
? pipeline_name || pipeline_id
|
||||
: parser_id === 'naive'
|
||||
? 'general'
|
||||
: parser_id}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="capitalize">
|
||||
{pipeline_id
|
||||
? pipeline_name || pipeline_id
|
||||
: parser_id === 'naive'
|
||||
? 'general'
|
||||
: parser_id}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={handleShowChangeParserModal}>
|
||||
{t('knowledgeDetails.dataPipeline')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export function ParsingStatusCell({
|
||||
record,
|
||||
showLog,
|
||||
}: {
|
||||
record: IDocumentInfo;
|
||||
showLog: (record: IDocumentInfo) => void;
|
||||
} & UseChangeDocumentParserShowType) {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
run,
|
||||
parser_id,
|
||||
pipeline_id,
|
||||
pipeline_name,
|
||||
progress,
|
||||
chunk_num,
|
||||
id,
|
||||
} = record;
|
||||
const { run, progress, chunk_num, id } = record;
|
||||
const operationIcon = IconMap[run];
|
||||
const p = Number((progress * 100).toFixed(2));
|
||||
const {
|
||||
@@ -86,10 +125,6 @@ export function ParsingStatusCell({
|
||||
handleRunDocumentByIds(record.id, isRunning, option);
|
||||
};
|
||||
|
||||
const handleShowChangeParserModal = useCallback(() => {
|
||||
showChangeParserModal(record);
|
||||
}, [record, showChangeParserModal]);
|
||||
|
||||
const showParse = useMemo(() => {
|
||||
return record.type !== DocumentType.Virtual;
|
||||
}, [record]);
|
||||
@@ -103,71 +138,25 @@ export function ParsingStatusCell({
|
||||
data-testid="document-parse-status"
|
||||
data-state={ParseStatusStateMap[run] ?? 'unknown'}
|
||||
>
|
||||
<div className="text-ellipsis w-[100px] flex items-center justify-between">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="border-none truncate max-w-32 cursor-pointer px-2 py-1 rounded-sm hover:bg-bg-card">
|
||||
{pipeline_id
|
||||
? pipeline_name || pipeline_id
|
||||
: parser_id === 'naive'
|
||||
? 'general'
|
||||
: parser_id}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
{pipeline_id
|
||||
? pipeline_name || pipeline_id
|
||||
: parser_id === 'naive'
|
||||
? 'general'
|
||||
: parser_id}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={handleShowChangeParserModal}>
|
||||
{t('knowledgeDetails.dataPipeline')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
{showParse && (
|
||||
<div className="flex items-center gap-3">
|
||||
<Separator orientation="vertical" className="h-2.5" />
|
||||
{!isParserRunning(run) && (
|
||||
// <ReparseDialog
|
||||
// hidden={isZeroChunk || isRunning}
|
||||
// handleOperationIconClick={handleOperationIconClick}
|
||||
// chunk_num={chunk_num}
|
||||
// >
|
||||
<div
|
||||
className="cursor-pointer flex items-center gap-3"
|
||||
onClick={() => {
|
||||
showReparseDialogModal();
|
||||
}}
|
||||
>
|
||||
{!isParserRunning(run) && operationIcon}
|
||||
</div>
|
||||
// {/* </ReparseDialog> */}
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<Separator orientation="vertical" className="h-[1em]" />
|
||||
|
||||
{isParserRunning(run) ? (
|
||||
<>
|
||||
<div
|
||||
className="flex items-center gap-1 cursor-pointer"
|
||||
<Button
|
||||
size="auto"
|
||||
variant="static"
|
||||
onClick={() => handleShowLog(record)}
|
||||
>
|
||||
<Progress value={p} className="h-1 flex-1 min-w-10" />
|
||||
{p}%
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer flex items-center gap-3"
|
||||
onClick={() => {
|
||||
showReparseDialogModal();
|
||||
}}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() => showReparseDialogModal()}
|
||||
// onClick={
|
||||
// isZeroChunk || isRunning
|
||||
// ? handleOperationIconClick(false)
|
||||
@@ -175,13 +164,22 @@ export function ParsingStatusCell({
|
||||
// }
|
||||
>
|
||||
{operationIcon}
|
||||
</div>
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<ParsingCard
|
||||
record={record}
|
||||
handleShowLog={handleShowLog}
|
||||
></ParsingCard>
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() => {
|
||||
showReparseDialogModal();
|
||||
}}
|
||||
>
|
||||
{operationIcon}
|
||||
</Button>
|
||||
|
||||
<ParsingCard record={record} handleShowLog={handleShowLog} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
} from '@/hooks/use-document-request';
|
||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||
import {
|
||||
Ban,
|
||||
CircleCheck,
|
||||
CircleX,
|
||||
Cylinder,
|
||||
Play,
|
||||
Trash2,
|
||||
LucideCircleX,
|
||||
LucideCylinder,
|
||||
LucidePlayCircle,
|
||||
LucideToggleLeft,
|
||||
LucideToggleRight,
|
||||
LucideTrash2,
|
||||
} from 'lucide-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -117,36 +117,36 @@ export function useBulkOperateDataset({
|
||||
{
|
||||
id: 'enabled',
|
||||
label: t('knowledgeDetails.enabled'),
|
||||
icon: <CircleCheck />,
|
||||
icon: <LucideToggleRight />,
|
||||
onClick: handleEnableClick,
|
||||
},
|
||||
{
|
||||
id: 'disabled',
|
||||
label: t('knowledgeDetails.disabled'),
|
||||
icon: <Ban />,
|
||||
icon: <LucideToggleLeft />,
|
||||
onClick: handleDisableClick,
|
||||
},
|
||||
{
|
||||
id: 'run',
|
||||
label: t('knowledgeDetails.run'),
|
||||
icon: <Play />,
|
||||
icon: <LucidePlayCircle />,
|
||||
onClick: () => showModal(),
|
||||
},
|
||||
{
|
||||
id: 'cancel',
|
||||
label: t('knowledgeDetails.cancel'),
|
||||
icon: <CircleX />,
|
||||
icon: <LucideCircleX />,
|
||||
onClick: handleCancelClick,
|
||||
},
|
||||
{
|
||||
id: 'batch-metadata',
|
||||
label: t('knowledgeDetails.metadata.metadata'),
|
||||
icon: <Cylinder />,
|
||||
icon: <LucideCylinder />,
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
label: t('common.delete'),
|
||||
icon: <Trash2 />,
|
||||
icon: <LucideTrash2 />,
|
||||
onClick: async () => {
|
||||
const code = await handleDelete();
|
||||
if (code === 0) {
|
||||
|
||||
@@ -11,15 +11,14 @@ 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 { useDataSourceInfo } from '@/pages/user-setting/data-source/constant';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { ColumnDef } from '@tanstack/table-core';
|
||||
import { ArrowUpDown, MonitorUp } from 'lucide-react';
|
||||
import { ArrowUpDown } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MetadataType } from '../components/metedata/constant';
|
||||
import { ShowManageMetadataModalProps } from '../components/metedata/interface';
|
||||
import { DatasetActionCell } from './dataset-action-cell';
|
||||
import { ParsingStatusCell } from './parsing-status-cell';
|
||||
import { ParseDropdownButton, ParsingStatusCell } from './parsing-status-cell';
|
||||
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
|
||||
import { UseRenameDocumentShowType } from './use-rename-document';
|
||||
|
||||
@@ -38,7 +37,7 @@ export function useDatasetTableColumns({
|
||||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'knowledgeDetails',
|
||||
});
|
||||
const { dataSourceInfo } = useDataSourceInfo();
|
||||
// const { dataSourceInfo } = useDataSourceInfo();
|
||||
const { navigateToChunkParsedResult } = useNavigatePage();
|
||||
const { setDocumentStatus } = useSetDocumentStatus();
|
||||
|
||||
@@ -47,6 +46,7 @@ export function useDatasetTableColumns({
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
className="size-3"
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
@@ -57,6 +57,7 @@ export function useDatasetTableColumns({
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
className="size-3"
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
@@ -69,14 +70,19 @@ export function useDatasetTableColumns({
|
||||
accessorKey: 'name',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="transparent"
|
||||
className="border-none"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{t('name')}
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === 'asc')
|
||||
}
|
||||
>
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
meta: { cellClassName: 'max-w-[20vw]' },
|
||||
@@ -87,7 +93,7 @@ export function useDatasetTableColumns({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className="flex gap-2 cursor-pointer"
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
onClick={navigateToChunkParsedResult(
|
||||
row.original.id,
|
||||
row.original.kb_id,
|
||||
@@ -108,22 +114,31 @@ export function useDatasetTableColumns({
|
||||
accessorKey: 'create_time',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="transparent"
|
||||
className="border-none"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{t('uploadDate')}
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === 'asc')
|
||||
}
|
||||
>
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<div className="lowercase">
|
||||
<time
|
||||
className="lowercase"
|
||||
dateTime={new Date(row.getValue('create_time')).toISOString()}
|
||||
>
|
||||
{formatDate(row.getValue('create_time'))}
|
||||
</div>
|
||||
</time>
|
||||
),
|
||||
},
|
||||
/*
|
||||
{
|
||||
accessorKey: 'source_from',
|
||||
header: t('source'),
|
||||
@@ -146,6 +161,7 @@ export function useDatasetTableColumns({
|
||||
</div>
|
||||
),
|
||||
},
|
||||
*/
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: t('enabled'),
|
||||
@@ -174,8 +190,9 @@ export function useDatasetTableColumns({
|
||||
cell: ({ row }) => {
|
||||
const length = Object.keys(row.getValue('meta_fields') || {}).length;
|
||||
return (
|
||||
<div
|
||||
className="capitalize cursor-pointer"
|
||||
<Button
|
||||
variant="static"
|
||||
size="auto"
|
||||
onClick={() => {
|
||||
showManageMetadataModal({
|
||||
// metadata: util.JSONToMetaDataTableData(
|
||||
@@ -209,7 +226,7 @@ export function useDatasetTableColumns({
|
||||
}}
|
||||
>
|
||||
{length + ' fields'}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -217,13 +234,25 @@ export function useDatasetTableColumns({
|
||||
accessorKey: 'run',
|
||||
header: t('Parse'),
|
||||
// meta: { cellClassName: 'min-w-[20vw]' },
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<ParseDropdownButton
|
||||
record={row.original}
|
||||
showChangeParserModal={showChangeParserModal}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'run-status',
|
||||
header: '',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<ParsingStatusCell
|
||||
record={row.original}
|
||||
showChangeParserModal={showChangeParserModal}
|
||||
showLog={showLog}
|
||||
></ParsingStatusCell>
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -238,7 +267,7 @@ export function useDatasetTableColumns({
|
||||
<DatasetActionCell
|
||||
record={record}
|
||||
showRenameModal={showRenameModal}
|
||||
></DatasetActionCell>
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ export function SeeAllCard() {
|
||||
return (
|
||||
<Card
|
||||
className="w-full flex-none h-full cursor-pointer"
|
||||
onClick={navigateToDatasetList}
|
||||
onClick={() => navigateToDatasetList({ isCreate: false })}
|
||||
>
|
||||
<CardContent className="p-2.5 pt-1 w-full h-full flex items-center justify-center gap-1.5 text-text-secondary">
|
||||
{t('common.seeAll')} <ChevronRight className="size-4" />
|
||||
|
||||
@@ -20,14 +20,16 @@ export function ApplicationCard({
|
||||
moreDropdown,
|
||||
}: ApplicationCardProps) {
|
||||
return (
|
||||
<Card className="w-[264px]" onClick={onClick}>
|
||||
<CardContent className="p-2.5 group flex justify-between w-full">
|
||||
<Card className="w-[264px]" onClick={onClick} as="article">
|
||||
<CardContent className="p-2.5 group flex justify-between w-full">
|
||||
<div className="flex items-center gap-2.5 w-full">
|
||||
<RAGFlowAvatar
|
||||
className="size-14 rounded-lg"
|
||||
avatar={app.avatar}
|
||||
name={app.title || 'CN'}
|
||||
></RAGFlowAvatar>
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<div className="flex-1">
|
||||
<h3 className="text-sm font-normal line-clamp-1 mb-1 text-ellipsis w-[160px] overflow-hidden">
|
||||
{app.title}
|
||||
@@ -37,6 +39,7 @@ export function ApplicationCard({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{moreDropdown}
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -49,7 +52,11 @@ export type SeeAllAppCardProps = {
|
||||
|
||||
export function SeeAllAppCard({ click }: SeeAllAppCardProps) {
|
||||
return (
|
||||
<Card className="w-full min-h-[76px] cursor-pointer" onClick={click}>
|
||||
<Card
|
||||
className="w-full min-h-[76px] cursor-pointer"
|
||||
onClick={click}
|
||||
tabIndex={0}
|
||||
>
|
||||
<CardContent className="p-2.5 pt-1 w-full h-full flex items-center justify-center gap-1.5 text-text-secondary">
|
||||
{t('common.seeAll')} <ChevronRight className="size-4" />
|
||||
</CardContent>
|
||||
|
||||
@@ -47,10 +47,10 @@ export function Applications() {
|
||||
|
||||
const options = useMemo(
|
||||
() => [
|
||||
{ value: Routes.Chats, label: t('chat.chatApps') },
|
||||
{ value: Routes.Searches, label: t('search.searchApps') },
|
||||
{ value: Routes.Chats, label: t('header.chat') },
|
||||
{ value: Routes.Searches, label: t('header.search') },
|
||||
{ value: Routes.Agents, label: t('header.flow') },
|
||||
{ value: Routes.Memories, label: t('memories.memory') },
|
||||
{ value: Routes.Memories, label: t('header.memories') },
|
||||
],
|
||||
[t],
|
||||
);
|
||||
@@ -63,55 +63,57 @@ export function Applications() {
|
||||
|
||||
return (
|
||||
<section className="mt-12">
|
||||
<div className="flex justify-between items-center mb-5">
|
||||
<h2 className="text-2xl font-semibold flex gap-2.5">
|
||||
<header className="flex justify-between items-center mb-2.5">
|
||||
<h2 className="text-2xl font-semibold">
|
||||
<HomeIcon
|
||||
imgClass="me-2.5"
|
||||
name={`${IconMap[val as keyof typeof IconMap]}`}
|
||||
width={'32'}
|
||||
width={24}
|
||||
/>
|
||||
{options.find((x) => x.value === val)?.label}
|
||||
</h2>
|
||||
|
||||
<Segmented
|
||||
buttonSize="sm"
|
||||
options={options}
|
||||
value={val}
|
||||
onChange={handleChange}
|
||||
buttonSize="xl"
|
||||
// className="bg-bg-card border border-border-button rounded-lg"
|
||||
// activeClassName="bg-text-primary border-none rounded-lg"
|
||||
></Segmented>
|
||||
</div>
|
||||
/>
|
||||
</header>
|
||||
|
||||
{/* <div className="flex flex-wrap gap-4"> */}
|
||||
<CardSineLineContainer>
|
||||
{val === Routes.Agents && (
|
||||
<Agents
|
||||
setListLength={(length: number) => setListLength(length)}
|
||||
setLoading={(loading: boolean) => setLoading(loading)}
|
||||
></Agents>
|
||||
/>
|
||||
)}
|
||||
{val === Routes.Chats && (
|
||||
<ChatList
|
||||
setListLength={(length: number) => setListLength(length)}
|
||||
setLoading={(loading: boolean) => setLoading(loading)}
|
||||
></ChatList>
|
||||
/>
|
||||
)}
|
||||
{val === Routes.Searches && (
|
||||
<SearchList
|
||||
setListLength={(length: number) => setListLength(length)}
|
||||
setLoading={(loading: boolean) => setLoading(loading)}
|
||||
></SearchList>
|
||||
/>
|
||||
)}
|
||||
{val === Routes.Memories && (
|
||||
<MemoryList
|
||||
setListLength={(length: number) => setListLength(length)}
|
||||
setLoading={(loading: boolean) => setLoading(loading)}
|
||||
></MemoryList>
|
||||
/>
|
||||
)}
|
||||
{listLength > 0 && (
|
||||
<SeeAllAppCard
|
||||
click={() => handleNavigate({ isCreate: false })}
|
||||
></SeeAllAppCard>
|
||||
<SeeAllAppCard click={() => handleNavigate({ isCreate: false })} />
|
||||
)}
|
||||
</CardSineLineContainer>
|
||||
|
||||
{listLength <= 0 && !loading && (
|
||||
<EmptyAppCard
|
||||
type={EmptyTypeMap[val as keyof typeof EmptyTypeMap]}
|
||||
|
||||
@@ -42,9 +42,11 @@ export function Banner() {
|
||||
export function NextBanner() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<h1 className="text-5xl font-bold leading-normal">
|
||||
<span className="text-text-primary">{t('header.welcome')} </span>
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-l from-[#40EBE3] to-[#4A51FF]">
|
||||
<h1 className="text-5xl leading-normal">
|
||||
<span className="font-semibold text-text-primary">
|
||||
{t('header.welcome')}{' '}
|
||||
</span>
|
||||
<span className="font-bold text-transparent bg-clip-text bg-gradient-to-l from-[#40EBE3] to-[#4A51FF]">
|
||||
RAGFlow
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
@@ -26,12 +26,15 @@ export function Datasets() {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-6 flex gap-2.5 items-center">
|
||||
{/* <IconFont name="data" className="size-8"></IconFont> */}
|
||||
<HomeIcon name="datasets" width={'32'} />
|
||||
{t('header.dataset')}
|
||||
</h2>
|
||||
<div className="">
|
||||
<header>
|
||||
<h2 className="leading-8 text-2xl font-semibold mb-2.5">
|
||||
{/* <IconFont name="data" className="size-8"></IconFont> */}
|
||||
<HomeIcon imgClass="me-2.5" name="datasets" width={24} />
|
||||
{t('header.dataset')}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
{loading ? (
|
||||
<div className="flex-1">
|
||||
<CardSkeleton />
|
||||
@@ -40,15 +43,13 @@ export function Datasets() {
|
||||
<>
|
||||
{kbs?.length > 0 && (
|
||||
<CardSineLineContainer>
|
||||
{kbs
|
||||
?.slice(0, 6)
|
||||
.map((dataset) => (
|
||||
<DatasetCard
|
||||
key={dataset.id}
|
||||
dataset={dataset}
|
||||
showDatasetRenameModal={showDatasetRenameModal}
|
||||
></DatasetCard>
|
||||
))}
|
||||
{kbs?.slice(0, 6).map((dataset) => (
|
||||
<DatasetCard
|
||||
key={dataset.id}
|
||||
dataset={dataset}
|
||||
showDatasetRenameModal={showDatasetRenameModal}
|
||||
></DatasetCard>
|
||||
))}
|
||||
{
|
||||
<SeeAllAppCard
|
||||
click={() => navigateToDatasetList({ isCreate: false })}
|
||||
@@ -66,13 +67,14 @@ export function Datasets() {
|
||||
// </div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{datasetRenameVisible && (
|
||||
<RenameDialog
|
||||
hideModal={hideDatasetRenameModal}
|
||||
onOk={onDatasetRenameOk}
|
||||
initialName={initialDatasetName}
|
||||
loading={datasetRenameLoading}
|
||||
></RenameDialog>
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PageContainer } from '@/layouts/page-container';
|
||||
import { PageContainer } from '@/layouts/components/page-container';
|
||||
import { Applications } from './applications';
|
||||
import { NextBanner } from './banner';
|
||||
import { Datasets } from './datasets';
|
||||
@@ -6,12 +6,14 @@ import { Datasets } from './datasets';
|
||||
const Home = () => {
|
||||
return (
|
||||
<PageContainer>
|
||||
<header className="mb-8">
|
||||
<NextBanner />
|
||||
</header>
|
||||
<article>
|
||||
<header className="mb-8">
|
||||
<NextBanner />
|
||||
</header>
|
||||
|
||||
<Datasets />
|
||||
<Applications />
|
||||
<Datasets />
|
||||
<Applications />
|
||||
</article>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { IClientConversation } from '@/interfaces/database/chat';
|
||||
import { NextLayoutContainer } from '@/layouts/next';
|
||||
import { RootLayoutContainer } from '@/layouts/root-layout';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useMount } from 'ahooks';
|
||||
import { isEmpty } from 'lodash';
|
||||
@@ -106,7 +106,7 @@ export default function Chat() {
|
||||
}
|
||||
|
||||
return (
|
||||
<NextLayoutContainer>
|
||||
<RootLayoutContainer>
|
||||
<section className="h-full flex flex-col" data-testid="chat-detail">
|
||||
<article className="flex flex-1 min-h-0 pb-9">
|
||||
<Sessions handleConversationCardClick={handleSessionClick}></Sessions>
|
||||
@@ -146,6 +146,6 @@ export default function Chat() {
|
||||
</Card>
|
||||
</article>
|
||||
</section>
|
||||
</NextLayoutContainer>
|
||||
</RootLayoutContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IconFontFill } from '@/components/icon-font';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import ThemeToggle from '@/components/theme-toggle';
|
||||
import ThemeSwitch from '@/components/theme-switch';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Domain } from '@/constants/common';
|
||||
import { useSecondPathName } from '@/hooks/route-hook';
|
||||
@@ -103,15 +103,10 @@ export function SideBar() {
|
||||
<div className="mr-2 px-2 text-accent-primary rounded-md">
|
||||
{version}
|
||||
</div>
|
||||
<ThemeToggle />
|
||||
<ThemeSwitch />
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full gap-3 bg-bg-base border border-border-button"
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
|
||||
<Button block size="lg" variant="transparent" onClick={() => logout()}>
|
||||
{t('setting.logout')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user