### What problem does this PR solve? Feat: Modify the style of the dataset page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.0
| import weekday from 'dayjs/plugin/weekday'; | import weekday from 'dayjs/plugin/weekday'; | ||||
| import React, { ReactNode, useEffect, useState } from 'react'; | import React, { ReactNode, useEffect, useState } from 'react'; | ||||
| import { ThemeProvider, useTheme } from './components/theme-provider'; | import { ThemeProvider, useTheme } from './components/theme-provider'; | ||||
| import { SidebarProvider } from './components/ui/sidebar'; | |||||
| import { TooltipProvider } from './components/ui/tooltip'; | import { TooltipProvider } from './components/ui/tooltip'; | ||||
| import storage from './utils/authorization-util'; | import storage from './utils/authorization-util'; | ||||
| }} | }} | ||||
| locale={locale} | locale={locale} | ||||
| > | > | ||||
| <App>{children}</App> | |||||
| <SidebarProvider> | |||||
| <App>{children}</App> | |||||
| </SidebarProvider> | |||||
| <Sonner position={'top-right'} expand richColors closeButton></Sonner> | <Sonner position={'top-right'} expand richColors closeButton></Sonner> | ||||
| <Toaster /> | <Toaster /> | ||||
| </ConfigProvider> | </ConfigProvider> |
| import { FileIconMap } from '@/constants/file'; | |||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { getExtension } from '@/utils/document-util'; | |||||
| type IconFontType = { | type IconFontType = { | ||||
| name: string; | name: string; | ||||
| <use xlinkHref={`#icon-${name}`} /> | <use xlinkHref={`#icon-${name}`} /> | ||||
| </svg> | </svg> | ||||
| ); | ); | ||||
| export function FileIcon({ | |||||
| name, | |||||
| className, | |||||
| type, | |||||
| }: IconFontType & { type?: string }) { | |||||
| const isFolder = type === 'folder'; | |||||
| return ( | |||||
| <span className={cn('size-4', className)}> | |||||
| <IconFont | |||||
| name={isFolder ? 'file' : FileIconMap[getExtension(name)]} | |||||
| ></IconFont> | |||||
| </span> | |||||
| ); | |||||
| } |
| import { cn } from '@/lib/utils'; | |||||
| import * as AvatarPrimitive from '@radix-ui/react-avatar'; | import * as AvatarPrimitive from '@radix-ui/react-avatar'; | ||||
| import { random } from 'lodash'; | import { random } from 'lodash'; | ||||
| import { forwardRef } from 'react'; | import { forwardRef } from 'react'; | ||||
| React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & { | React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & { | ||||
| name?: string; | name?: string; | ||||
| avatar?: string; | avatar?: string; | ||||
| isPerson?: boolean; | |||||
| } | } | ||||
| >(({ name, avatar, ...props }, ref) => { | |||||
| >(({ name, avatar, isPerson = false, className, ...props }, ref) => { | |||||
| const index = random(0, 3); | const index = random(0, 3); | ||||
| console.log('🚀 ~ index:', index); | console.log('🚀 ~ index:', index); | ||||
| const value = Colors[index]; | const value = Colors[index]; | ||||
| return ( | return ( | ||||
| <Avatar ref={ref} {...props}> | |||||
| <Avatar | |||||
| ref={ref} | |||||
| {...props} | |||||
| className={cn(className, { 'rounded-md': !isPerson })} | |||||
| > | |||||
| <AvatarImage src={avatar} /> | <AvatarImage src={avatar} /> | ||||
| <AvatarFallback | <AvatarFallback | ||||
| className={`bg-gradient-to-b from-[${value.from}] to-[${value.to}]`} | |||||
| className={cn( | |||||
| `bg-gradient-to-b from-[${value.from}] to-[${value.to}]`, | |||||
| { 'rounded-md': !isPerson }, | |||||
| )} | |||||
| > | > | ||||
| {name?.slice(0, 1)} | {name?.slice(0, 1)} | ||||
| </AvatarFallback> | </AvatarFallback> |
| >(({ className, ...props }, ref) => ( | >(({ className, ...props }, ref) => ( | ||||
| <SwitchPrimitives.Root | <SwitchPrimitives.Root | ||||
| className={cn( | className={cn( | ||||
| 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-colors-background-core-standard data-[state=unchecked]:bg-colors-background-inverse-standard', | |||||
| 'peer inline-flex h-3.5 w-6 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-background-checked data-[state=unchecked]:bg-text-sub-title', | |||||
| className, | className, | ||||
| )} | )} | ||||
| {...props} | {...props} | ||||
| > | > | ||||
| <SwitchPrimitives.Thumb | <SwitchPrimitives.Thumb | ||||
| className={cn( | className={cn( | ||||
| 'pointer-events-none block h-5 w-5 rounded-full bg-colors-text-neutral-strong shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0', | |||||
| 'pointer-events-none block size-3 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-2 data-[state=unchecked]:translate-x-0', | |||||
| )} | )} | ||||
| /> | /> | ||||
| </SwitchPrimitives.Root> | </SwitchPrimitives.Root> |
| HTMLTableElement, | HTMLTableElement, | ||||
| React.HTMLAttributes<HTMLTableElement> | React.HTMLAttributes<HTMLTableElement> | ||||
| >(({ className, ...props }, ref) => ( | >(({ className, ...props }, ref) => ( | ||||
| <div className="relative w-full overflow-auto"> | |||||
| <div className="relative w-full overflow-auto rounded-2xl bg-background-card"> | |||||
| <table | <table | ||||
| ref={ref} | ref={ref} | ||||
| className={cn('w-full caption-bottom text-sm', className)} | |||||
| className={cn('w-full caption-bottom text-sm ', className)} | |||||
| {...props} | {...props} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <th | <th | ||||
| ref={ref} | ref={ref} | ||||
| className={cn( | className={cn( | ||||
| 'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0', | |||||
| 'h-12 px-4 text-left align-middle font-normal text-text-sub-title [&:has([role=checkbox])]:pr-0', | |||||
| className, | className, | ||||
| )} | )} | ||||
| {...props} | {...props} | ||||
| >(({ className, ...props }, ref) => ( | >(({ className, ...props }, ref) => ( | ||||
| <td | <td | ||||
| ref={ref} | ref={ref} | ||||
| className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} | |||||
| className={cn( | |||||
| 'p-4 align-middle [&:has([role=checkbox])]:pr-0 text-text-title font-normal', | |||||
| className, | |||||
| )} | |||||
| {...props} | {...props} | ||||
| /> | /> | ||||
| )); | )); |
| export const FileIconMap = { | |||||
| doc: 'doc', | |||||
| docx: 'doc', | |||||
| pdf: 'pdf', | |||||
| xls: 'excel', | |||||
| xlsx: 'excel', | |||||
| ppt: 'ppt', | |||||
| pptx: 'ppt', | |||||
| jpg: 'jpg', | |||||
| jpeg: 'jpg', | |||||
| png: 'png', | |||||
| txt: 'text', | |||||
| csv: 'pdf', | |||||
| md: 'md', | |||||
| }; |
| .ant-app { | .ant-app { | ||||
| height: 100%; | height: 100%; | ||||
| width: 100%; | |||||
| } | } | ||||
| /* Scroll bar stylings */ | /* Scroll bar stylings */ |
| embd_id: string; | embd_id: string; | ||||
| nickname: string; | nickname: string; | ||||
| operator_permission: number; | operator_permission: number; | ||||
| size: number; | |||||
| } | } | ||||
| export interface IKnowledgeResult { | export interface IKnowledgeResult { |
| return ( | return ( | ||||
| <div className="w-full"> | <div className="w-full"> | ||||
| <div className="rounded-md border"> | |||||
| <Table> | |||||
| <TableHeader> | |||||
| {table.getHeaderGroups().map((headerGroup) => ( | |||||
| <TableRow key={headerGroup.id}> | |||||
| {headerGroup.headers.map((header) => { | |||||
| return ( | |||||
| <TableHead key={header.id}> | |||||
| {header.isPlaceholder | |||||
| ? null | |||||
| : flexRender( | |||||
| header.column.columnDef.header, | |||||
| header.getContext(), | |||||
| )} | |||||
| </TableHead> | |||||
| ); | |||||
| })} | |||||
| <Table> | |||||
| <TableHeader> | |||||
| {table.getHeaderGroups().map((headerGroup) => ( | |||||
| <TableRow key={headerGroup.id}> | |||||
| {headerGroup.headers.map((header) => { | |||||
| return ( | |||||
| <TableHead key={header.id}> | |||||
| {header.isPlaceholder | |||||
| ? null | |||||
| : flexRender( | |||||
| header.column.columnDef.header, | |||||
| header.getContext(), | |||||
| )} | |||||
| </TableHead> | |||||
| ); | |||||
| })} | |||||
| </TableRow> | |||||
| ))} | |||||
| </TableHeader> | |||||
| <TableBody className="relative"> | |||||
| {table.getRowModel().rows?.length ? ( | |||||
| table.getRowModel().rows.map((row) => ( | |||||
| <TableRow | |||||
| key={row.id} | |||||
| data-state={row.getIsSelected() && 'selected'} | |||||
| > | |||||
| {row.getVisibleCells().map((cell) => ( | |||||
| <TableCell | |||||
| key={cell.id} | |||||
| className={cell.column.columnDef.meta?.cellClassName} | |||||
| > | |||||
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | |||||
| </TableCell> | |||||
| ))} | |||||
| </TableRow> | </TableRow> | ||||
| ))} | |||||
| </TableHeader> | |||||
| <TableBody className="relative"> | |||||
| {table.getRowModel().rows?.length ? ( | |||||
| table.getRowModel().rows.map((row) => ( | |||||
| <TableRow | |||||
| key={row.id} | |||||
| data-state={row.getIsSelected() && 'selected'} | |||||
| > | |||||
| {row.getVisibleCells().map((cell) => ( | |||||
| <TableCell | |||||
| key={cell.id} | |||||
| className={cell.column.columnDef.meta?.cellClassName} | |||||
| > | |||||
| {flexRender( | |||||
| cell.column.columnDef.cell, | |||||
| cell.getContext(), | |||||
| )} | |||||
| </TableCell> | |||||
| ))} | |||||
| </TableRow> | |||||
| )) | |||||
| ) : ( | |||||
| <TableRow> | |||||
| <TableCell | |||||
| colSpan={columns.length} | |||||
| className="h-24 text-center" | |||||
| > | |||||
| No results. | |||||
| </TableCell> | |||||
| </TableRow> | |||||
| )} | |||||
| </TableBody> | |||||
| </Table> | |||||
| </div> | |||||
| )) | |||||
| ) : ( | |||||
| <TableRow> | |||||
| <TableCell colSpan={columns.length} className="h-24 text-center"> | |||||
| No results. | |||||
| </TableCell> | |||||
| </TableRow> | |||||
| )} | |||||
| </TableBody> | |||||
| </Table> | |||||
| <div className="flex items-center justify-end space-x-2 py-4"> | <div className="flex items-center justify-end space-x-2 py-4"> | ||||
| <div className="flex-1 text-sm text-muted-foreground"> | <div className="flex-1 text-sm text-muted-foreground"> | ||||
| {table.getFilteredSelectedRowModel().rows.length} of{' '} | {table.getFilteredSelectedRowModel().rows.length} of{' '} |
| }); | }); | ||||
| return ( | return ( | ||||
| <section className="p-8"> | |||||
| <section className="p-5"> | |||||
| <ListFilterBar | <ListFilterBar | ||||
| title="Dataset" | title="Dataset" | ||||
| onSearchChange={handleInputChange} | onSearchChange={handleInputChange} |
| import SvgIcon from '@/components/svg-icon'; | |||||
| import { FileIcon } from '@/components/icon-font'; | |||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { Checkbox } from '@/components/ui/checkbox'; | import { Checkbox } from '@/components/ui/checkbox'; | ||||
| import { Switch } from '@/components/ui/switch'; | import { Switch } from '@/components/ui/switch'; | ||||
| import { IDocumentInfo } from '@/interfaces/database/document'; | import { IDocumentInfo } from '@/interfaces/database/document'; | ||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { getExtension } from '@/utils/document-util'; | |||||
| import { ColumnDef } from '@tanstack/table-core'; | import { ColumnDef } from '@tanstack/table-core'; | ||||
| import { ArrowUpDown } from 'lucide-react'; | import { ArrowUpDown } from 'lucide-react'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| row.original.kb_id, | row.original.kb_id, | ||||
| )} | )} | ||||
| > | > | ||||
| <SvgIcon | |||||
| name={`file-icon/${getExtension(name)}`} | |||||
| width={24} | |||||
| ></SvgIcon> | |||||
| <FileIcon name={name}></FileIcon> | |||||
| <span className={cn('truncate')}>{name}</span> | <span className={cn('truncate')}>{name}</span> | ||||
| </div> | </div> | ||||
| </TooltipTrigger> | </TooltipTrigger> |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |||||
| import { RAGFlowAvatar } from '@/components/ragflow-avatar'; | |||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { useSecondPathName } from '@/hooks/route-hook'; | import { useSecondPathName } from '@/hooks/route-hook'; | ||||
| import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; | import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; | ||||
| import { cn } from '@/lib/utils'; | |||||
| import { cn, formatBytes } from '@/lib/utils'; | |||||
| import { Routes } from '@/routes'; | import { Routes } from '@/routes'; | ||||
| import { formatDate } from '@/utils/date'; | |||||
| import { Banknote, LayoutGrid, User } from 'lucide-react'; | |||||
| import { formatPureDate } from '@/utils/date'; | |||||
| import { Banknote, Database, FileSearch2 } from 'lucide-react'; | |||||
| import { useMemo } from 'react'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { useHandleMenuClick } from './hooks'; | import { useHandleMenuClick } from './hooks'; | ||||
| const items = [ | |||||
| { icon: User, label: 'Dataset', key: Routes.DatasetBase }, | |||||
| { | |||||
| icon: LayoutGrid, | |||||
| label: 'Retrieval testing', | |||||
| key: Routes.DatasetTesting, | |||||
| }, | |||||
| { icon: Banknote, label: 'Settings', key: Routes.DatasetSetting }, | |||||
| ]; | |||||
| export function SideBar() { | export function SideBar() { | ||||
| const pathName = useSecondPathName(); | const pathName = useSecondPathName(); | ||||
| const { handleMenuClick } = useHandleMenuClick(); | const { handleMenuClick } = useHandleMenuClick(); | ||||
| const { data } = useFetchKnowledgeBaseConfiguration(); | const { data } = useFetchKnowledgeBaseConfiguration(); | ||||
| const { t } = useTranslation(); | |||||
| return ( | |||||
| <aside className="w-60 relative border-r "> | |||||
| <div className="p-6 space-y-2 border-b"> | |||||
| <Avatar className="size-20 rounded-lg"> | |||||
| <AvatarImage src={data.avatar} /> | |||||
| <AvatarFallback className="rounded-lg">CN</AvatarFallback> | |||||
| </Avatar> | |||||
| const items = useMemo(() => { | |||||
| return [ | |||||
| { | |||||
| icon: Database, | |||||
| label: t(`knowledgeDetails.dataset`), | |||||
| key: Routes.DatasetBase, | |||||
| }, | |||||
| { | |||||
| icon: FileSearch2, | |||||
| label: t(`knowledgeDetails.testing`), | |||||
| key: Routes.DatasetTesting, | |||||
| }, | |||||
| { | |||||
| icon: Banknote, | |||||
| label: t(`knowledgeDetails.configuration`), | |||||
| key: Routes.DatasetSetting, | |||||
| }, | |||||
| ]; | |||||
| }, [t]); | |||||
| <h3 className="text-lg font-semibold mb-2 line-clamp-1">{data.name}</h3> | |||||
| <div className="text-sm opacity-80"> | |||||
| {data.doc_num} files | {data.chunk_num} chunks | |||||
| </div> | |||||
| <div className="text-sm opacity-80"> | |||||
| Created {formatDate(data.create_time)} | |||||
| return ( | |||||
| <aside className="relative p-5 space-y-8"> | |||||
| <div className="flex gap-2.5 max-w-[200px] items-center"> | |||||
| <RAGFlowAvatar | |||||
| avatar={data.avatar} | |||||
| name={data.name} | |||||
| className="size-16" | |||||
| ></RAGFlowAvatar> | |||||
| <div className=" text-text-sub-title text-xs space-y-1"> | |||||
| <h3 className="text-lg font-semibold line-clamp-1 text-text-title"> | |||||
| {data.name} | |||||
| </h3> | |||||
| <div className="flex justify-between"> | |||||
| <span>{data.doc_num} files</span> | |||||
| <span>{formatBytes(data.size)}</span> | |||||
| </div> | |||||
| <div>Created {formatPureDate(data.create_time)}</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="mt-4"> | |||||
| <div className="w-[200px] flex flex-col gap-5"> | |||||
| {items.map((item, itemIdx) => { | {items.map((item, itemIdx) => { | ||||
| const active = '/' + pathName === item.key; | const active = '/' + pathName === item.key; | ||||
| return ( | return ( | ||||
| <Button | <Button | ||||
| key={itemIdx} | key={itemIdx} | ||||
| variant={active ? 'secondary' : 'ghost'} | variant={active ? 'secondary' : 'ghost'} | ||||
| className={cn('w-full justify-start gap-2.5 p-6 relative')} | |||||
| className={cn( | |||||
| 'w-full justify-start gap-2.5 px-3 relative h-10 text-text-sub-title-invert', | |||||
| { | |||||
| 'bg-background-card': active, | |||||
| 'text-text-title': active, | |||||
| }, | |||||
| )} | |||||
| onClick={handleMenuClick(item.key)} | onClick={handleMenuClick(item.key)} | ||||
| > | > | ||||
| <item.icon className="w-6 h-6" /> | |||||
| <item.icon className="size-4" /> | |||||
| <span>{item.label}</span> | <span>{item.label}</span> | ||||
| </Button> | </Button> | ||||
| ); | ); |
| import { ArrowUpDown } from 'lucide-react'; | import { ArrowUpDown } from 'lucide-react'; | ||||
| import * as React from 'react'; | import * as React from 'react'; | ||||
| import { FileIcon } from '@/components/icon-font'; | |||||
| import { RenameDialog } from '@/components/rename-dialog'; | import { RenameDialog } from '@/components/rename-dialog'; | ||||
| import SvgIcon from '@/components/svg-icon'; | |||||
| import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; | import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; | ||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { Checkbox } from '@/components/ui/checkbox'; | import { Checkbox } from '@/components/ui/checkbox'; | ||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { formatFileSize } from '@/utils/common-util'; | import { formatFileSize } from '@/utils/common-util'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { getExtension } from '@/utils/document-util'; | |||||
| import { pick } from 'lodash'; | import { pick } from 'lodash'; | ||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| <Tooltip> | <Tooltip> | ||||
| <TooltipTrigger asChild> | <TooltipTrigger asChild> | ||||
| <div className="flex gap-2"> | <div className="flex gap-2"> | ||||
| <SvgIcon | |||||
| name={`file-icon/${isFolder ? 'folder' : getExtension(name)}`} | |||||
| width={24} | |||||
| ></SvgIcon> | |||||
| <span className="size-4"> | |||||
| <FileIcon name={name} type={type}></FileIcon> | |||||
| </span> | |||||
| <span | <span | ||||
| className={cn('truncate', { ['cursor-pointer']: isFolder })} | className={cn('truncate', { ['cursor-pointer']: isFolder })} | ||||
| onClick={handleNameClick} | onClick={handleNameClick} | ||||
| return ( | return ( | ||||
| <div className="w-full"> | <div className="w-full"> | ||||
| <div className="rounded-md border"> | |||||
| <Table> | |||||
| <TableHeader> | |||||
| {table.getHeaderGroups().map((headerGroup) => ( | |||||
| <TableRow key={headerGroup.id}> | |||||
| {headerGroup.headers.map((header) => { | |||||
| return ( | |||||
| <TableHead key={header.id}> | |||||
| {header.isPlaceholder | |||||
| ? null | |||||
| : flexRender( | |||||
| header.column.columnDef.header, | |||||
| header.getContext(), | |||||
| )} | |||||
| </TableHead> | |||||
| ); | |||||
| })} | |||||
| <Table> | |||||
| <TableHeader> | |||||
| {table.getHeaderGroups().map((headerGroup) => ( | |||||
| <TableRow key={headerGroup.id}> | |||||
| {headerGroup.headers.map((header) => { | |||||
| return ( | |||||
| <TableHead key={header.id}> | |||||
| {header.isPlaceholder | |||||
| ? null | |||||
| : flexRender( | |||||
| header.column.columnDef.header, | |||||
| header.getContext(), | |||||
| )} | |||||
| </TableHead> | |||||
| ); | |||||
| })} | |||||
| </TableRow> | |||||
| ))} | |||||
| </TableHeader> | |||||
| <TableBody> | |||||
| {loading ? ( | |||||
| <TableSkeleton columnsLength={columns.length}></TableSkeleton> | |||||
| ) : table.getRowModel().rows?.length ? ( | |||||
| table.getRowModel().rows.map((row) => ( | |||||
| <TableRow | |||||
| key={row.id} | |||||
| data-state={row.getIsSelected() && 'selected'} | |||||
| > | |||||
| {row.getVisibleCells().map((cell) => ( | |||||
| <TableCell | |||||
| key={cell.id} | |||||
| className={cell.column.columnDef.meta?.cellClassName} | |||||
| > | |||||
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | |||||
| </TableCell> | |||||
| ))} | |||||
| </TableRow> | </TableRow> | ||||
| ))} | |||||
| </TableHeader> | |||||
| <TableBody> | |||||
| {loading ? ( | |||||
| <TableSkeleton columnsLength={columns.length}></TableSkeleton> | |||||
| ) : table.getRowModel().rows?.length ? ( | |||||
| table.getRowModel().rows.map((row) => ( | |||||
| <TableRow | |||||
| key={row.id} | |||||
| data-state={row.getIsSelected() && 'selected'} | |||||
| > | |||||
| {row.getVisibleCells().map((cell) => ( | |||||
| <TableCell | |||||
| key={cell.id} | |||||
| className={cell.column.columnDef.meta?.cellClassName} | |||||
| > | |||||
| {flexRender( | |||||
| cell.column.columnDef.cell, | |||||
| cell.getContext(), | |||||
| )} | |||||
| </TableCell> | |||||
| ))} | |||||
| </TableRow> | |||||
| )) | |||||
| ) : ( | |||||
| <TableEmpty columnsLength={columns.length}></TableEmpty> | |||||
| )} | |||||
| </TableBody> | |||||
| </Table> | |||||
| </div> | |||||
| )) | |||||
| ) : ( | |||||
| <TableEmpty columnsLength={columns.length}></TableEmpty> | |||||
| )} | |||||
| </TableBody> | |||||
| </Table> | |||||
| <div className="flex items-center justify-end space-x-2 py-4"> | <div className="flex items-center justify-end space-x-2 py-4"> | ||||
| <div className="flex-1 text-sm text-muted-foreground"> | <div className="flex-1 text-sm text-muted-foreground"> | ||||
| {table.getFilteredSelectedRowModel().rows.length} of {total} row(s) | {table.getFilteredSelectedRowModel().rows.length} of {total} row(s) |
| > | > | ||||
| <DropdownMenu> | <DropdownMenu> | ||||
| <DropdownMenuTrigger asChild> | <DropdownMenuTrigger asChild> | ||||
| <Button variant={'tertiary'} size={'sm'}> | |||||
| <Button> | |||||
| <Upload /> | <Upload /> | ||||
| {t('knowledgeDetails.addFile')} | {t('knowledgeDetails.addFile')} | ||||
| </Button> | </Button> |
| 'text-title-invert': 'var(--text-title-invert)', | 'text-title-invert': 'var(--text-title-invert)', | ||||
| 'background-header-bar': 'var(--background-header-bar)', | 'background-header-bar': 'var(--background-header-bar)', | ||||
| 'background-card': 'var(--background-card)', | 'background-card': 'var(--background-card)', | ||||
| 'background-checked': 'var(--background-checked)', | |||||
| primary: { | primary: { | ||||
| DEFAULT: 'hsl(var(--primary))', | DEFAULT: 'hsl(var(--primary))', |
| --background-header-bar: rgba(11, 11, 12, 1); | --background-header-bar: rgba(11, 11, 12, 1); | ||||
| --text-title-invert: rgba(255, 255, 255, 1); | --text-title-invert: rgba(255, 255, 255, 1); | ||||
| --background-card: rgba(22, 22, 24, 0.05); | --background-card: rgba(22, 22, 24, 0.05); | ||||
| --background-checked: rgba(76, 164, 231, 1); | |||||
| } | } | ||||
| .dark { | .dark { | ||||
| --text-title-invert: rgba(22, 22, 24, 1); | --text-title-invert: rgba(22, 22, 24, 1); | ||||
| --background-card: rgba(255, 255, 255, 0.05); | --background-card: rgba(255, 255, 255, 0.05); | ||||
| --background-checked: rgba(76, 164, 231, 1); | |||||
| } | } | ||||
| } | } | ||||