From a75cda49572bbf051330b8cfadad665e208a9176 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 16 Jan 2025 11:35:39 +0800 Subject: [PATCH] Feat: Add LinkToDatasetDialog #3221 (#4500) ### What problem does this PR solve? Feat: Add LinkToDatasetDialog #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/ui/badge.tsx | 2 +- web/src/components/ui/button.tsx | 3 +- web/src/components/ui/loading-button.tsx | 2 + web/src/hooks/knowledge-hooks.ts | 11 ++ web/src/pages/files/action-cell.tsx | 61 ++++++++ web/src/pages/files/files-table.tsx | 87 ++++++------ web/src/pages/files/hooks.ts | 6 +- .../pages/files/link-to-dataset-dialog.tsx | 130 ++++++++++++++++++ web/tailwind.config.js | 5 + web/tailwind.css | 5 + 10 files changed, 267 insertions(+), 45 deletions(-) create mode 100644 web/src/pages/files/action-cell.tsx create mode 100644 web/src/pages/files/link-to-dataset-dialog.tsx diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx index 2ccf66c25d3..2bdbfb46be0 100644 --- a/web/src/components/ui/badge.tsx +++ b/web/src/components/ui/badge.tsx @@ -16,7 +16,7 @@ const badgeVariants = cva( 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', outline: 'text-foreground', tertiary: - 'border-transparent bg-colors-text-core-standard text-foreground hover:bg-colors-text-core-standard/80', + 'border-transparent bg-colors-background-core-strong text-colors-text-persist-light hover:bg-colors-background-core-strong/80', }, }, defaultVariants: { diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index 1bf8b4699e1..962c1eb1d26 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -19,7 +19,7 @@ const buttonVariants = cva( ghost: 'hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline', tertiary: - 'bg-colors-text-core-standard text-foreground hover:bg-colors-text-core-standard/80', + 'bg-colors-background-sentiment-solid-primary text-colors-text-persist-light hover:bg-colors-background-sentiment-solid-primary/80', icon: 'bg-colors-background-inverse-standard text-foreground hover:bg-colors-background-inverse-standard/80', }, size: { @@ -27,6 +27,7 @@ const buttonVariants = cva( sm: 'h-9 rounded-md px-3', lg: 'h-11 rounded-md px-8', icon: 'h-10 w-10', + auto: 'h-full px-1', }, }, defaultVariants: { diff --git a/web/src/components/ui/loading-button.tsx b/web/src/components/ui/loading-button.tsx index 7bc95247d0e..570b4c8ecda 100644 --- a/web/src/components/ui/loading-button.tsx +++ b/web/src/components/ui/loading-button.tsx @@ -20,6 +20,8 @@ const buttonVariants = cva( 'bg-secondary text-secondary-foreground hover:bg-secondary/80', ghost: 'hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline', + tertiary: + 'bg-colors-background-sentiment-solid-primary text-colors-text-persist-light hover:bg-colors-background-sentiment-solid-primary/80', }, size: { default: 'h-10 px-4 py-2', diff --git a/web/src/hooks/knowledge-hooks.ts b/web/src/hooks/knowledge-hooks.ts index f28d4113e71..9ff7bbb745e 100644 --- a/web/src/hooks/knowledge-hooks.ts +++ b/web/src/hooks/knowledge-hooks.ts @@ -72,6 +72,17 @@ export const useFetchKnowledgeList = ( return { list: data, loading }; }; +export const useSelectKnowledgeOptions = () => { + const { list } = useFetchKnowledgeList(); + + const options = list?.map((item) => ({ + label: item.name, + value: item.id, + })); + + return options; +}; + export const useInfiniteFetchKnowledgeList = () => { const { searchString, handleInputChange } = useHandleSearchChange(); const debouncedSearchString = useDebounce(searchString, { wait: 500 }); diff --git a/web/src/pages/files/action-cell.tsx b/web/src/pages/files/action-cell.tsx new file mode 100644 index 00000000000..f56d596e606 --- /dev/null +++ b/web/src/pages/files/action-cell.tsx @@ -0,0 +1,61 @@ +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { IFile } from '@/interfaces/database/file-manager'; +import { CellContext } from '@tanstack/react-table'; +import { EllipsisVertical, Link2, Trash2 } from 'lucide-react'; +import { useCallback } from 'react'; +import { UseHandleConnectToKnowledgeReturnType } from './hooks'; + +type IProps = Pick, 'row'> & + Pick; + +export function ActionCell({ row, showConnectToKnowledgeModal }: IProps) { + const record = row.original; + + const handleShowConnectToKnowledgeModal = useCallback(() => { + showConnectToKnowledgeModal(record); + }, [record, showConnectToKnowledgeModal]); + + return ( +
+ + + + + + + + + + Actions + navigator.clipboard.writeText(record.id)} + > + Copy payment ID + + + View customer + View payment details + + +
+ ); +} diff --git a/web/src/pages/files/files-table.tsx b/web/src/pages/files/files-table.tsx index 400f0ba74c1..d22420eb1e3 100644 --- a/web/src/pages/files/files-table.tsx +++ b/web/src/pages/files/files-table.tsx @@ -11,22 +11,14 @@ import { getSortedRowModel, useReactTable, } from '@tanstack/react-table'; -import { ArrowUpDown, MoreHorizontal, Pencil } from 'lucide-react'; +import { ArrowUpDown } from 'lucide-react'; import * as React from 'react'; import SvgIcon from '@/components/svg-icon'; import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { Switch } from '@/components/ui/switch'; import { Table, TableBody, @@ -48,7 +40,9 @@ import { formatDate } from '@/utils/date'; import { getExtension } from '@/utils/document-util'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigateToOtherFolder } from './hooks'; +import { ActionCell } from './action-cell'; +import { useHandleConnectToKnowledge, useNavigateToOtherFolder } from './hooks'; +import { LinkToDatasetDialog } from './link-to-dataset-dialog'; export function FilesTable() { const [sorting, setSorting] = React.useState([]); @@ -62,6 +56,14 @@ export function FilesTable() { keyPrefix: 'fileManager', }); const navigateToOtherFolder = useNavigateToOtherFolder(); + const { + connectToKnowledgeVisible, + hideConnectToKnowledgeModal, + showConnectToKnowledgeModal, + initialConnectedIds, + onConnectToKnowledgeOk, + connectToKnowledgeLoading, + } = useHandleConnectToKnowledge(); const { pagination, data, loading, setPagination } = useFetchFileList(); @@ -176,44 +178,37 @@ export function FilesTable() { { accessorKey: 'kbs_info', header: t('knowledgeBase'), - cell: ({ row }) => ( - - ), + cell: ({ row }) => { + const value = row.getValue('kbs_info'); + return Array.isArray(value) ? ( +
+ {value?.slice(0, 2).map((x) => ( + + {x.kb_name} + + ))} + + {value.length > 2 && ( + + )} +
+ ) : ( + '' + ); + }, }, { id: 'actions', header: t('action'), enableHiding: false, cell: ({ row }) => { - const payment = row.original; - return ( -
- - - - - - - - Actions - navigator.clipboard.writeText(payment.id)} - > - Copy payment ID - - - View customer - View payment details - - -
+ ); }, }, @@ -338,6 +333,14 @@ export function FilesTable() { + {connectToKnowledgeVisible && ( + + )} ); } diff --git a/web/src/pages/files/hooks.ts b/web/src/pages/files/hooks.ts index d1145e5725d..9a891d711ba 100644 --- a/web/src/pages/files/hooks.ts +++ b/web/src/pages/files/hooks.ts @@ -224,7 +224,7 @@ export const useHandleConnectToKnowledge = () => { ); return { - initialValue, + initialConnectedIds: initialValue, connectToKnowledgeLoading: loading, onConnectToKnowledgeOk, connectToKnowledgeVisible, @@ -233,6 +233,10 @@ export const useHandleConnectToKnowledge = () => { }; }; +export type UseHandleConnectToKnowledgeReturnType = ReturnType< + typeof useHandleConnectToKnowledge +>; + export const useHandleBreadcrumbClick = () => { const navigate = useNavigate(); diff --git a/web/src/pages/files/link-to-dataset-dialog.tsx b/web/src/pages/files/link-to-dataset-dialog.tsx new file mode 100644 index 00000000000..42ddbb8c0f9 --- /dev/null +++ b/web/src/pages/files/link-to-dataset-dialog.tsx @@ -0,0 +1,130 @@ +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { LoadingButton } from '@/components/ui/loading-button'; +import { MultiSelect } from '@/components/ui/multi-select'; +import { useSelectKnowledgeOptions } from '@/hooks/knowledge-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Link2 } from 'lucide-react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { UseHandleConnectToKnowledgeReturnType } from './hooks'; + +const FormId = 'LinkToDatasetForm'; + +const FormSchema = z.object({ + knowledgeIds: z.array(z.string()).min(0, { + message: 'Username must be at least 1 characters.', + }), +}); + +function LinkToDatasetForm({ + initialConnectedIds, + onConnectToKnowledgeOk, +}: Pick< + UseHandleConnectToKnowledgeReturnType, + 'initialConnectedIds' | 'onConnectToKnowledgeOk' +>) { + const { t } = useTranslation(); + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + knowledgeIds: initialConnectedIds, + }, + }); + + const options = useSelectKnowledgeOptions(); + + function onSubmit(data: z.infer) { + onConnectToKnowledgeOk(data.knowledgeIds); + } + + // useEffect(() => { + // form.setValue('knowledgeIds', initialConnectedIds); // this is invalid + // }, [form, initialConnectedIds]); + + return ( +
+ + ( + + Name + + + + + + + )} + /> + + + ); +} + +export function LinkToDatasetDialog({ + hideModal, + initialConnectedIds, + onConnectToKnowledgeOk, + loading, +}: IModalProps & + Pick< + UseHandleConnectToKnowledgeReturnType, + 'initialConnectedIds' | 'onConnectToKnowledgeOk' + >) { + const { t } = useTranslation(); + return ( + + + + {t('fileManager.addToKnowledge')} + + + + +
+ Save +
+
+
+
+
+ ); +} diff --git a/web/tailwind.config.js b/web/tailwind.config.js index a819ad1baa3..94df6b51ce8 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -37,6 +37,7 @@ module.exports = { 'colors-text-neutral-standard': 'var(--colors-text-neutral-standard)', 'colors-text-functional-danger': 'var(--colors-text-functional-danger)', 'colors-text-inverse-strong': 'var(--colors-text-inverse-strong)', + 'colors-text-persist-light': 'var(--colors-text-persist-light)', primary: { DEFAULT: 'hsl(var(--primary))', @@ -156,6 +157,10 @@ module.exports = { DEFAULT: 'var(--colors-background-neutral-weak)', foreground: 'var(--background-inverse-standard-foreground)', }, + 'colors-background-sentiment-solid-primary': { + DEFAULT: 'var(--colors-background-sentiment-solid-primary)', + foreground: 'var(--background-inverse-standard-foreground)', + }, }, borderRadius: { lg: `var(--radius)`, diff --git a/web/tailwind.css b/web/tailwind.css index aff02989fb0..8a5c626e71b 100644 --- a/web/tailwind.css +++ b/web/tailwind.css @@ -42,6 +42,8 @@ --colors-background-inverse-weak: rgba(17, 16, 23, 0.1); --colors-background-neutral-standard: white; --colors-background-functional-solid-danger: rgba(222, 17, 53, 1); + --colors-background-core-strong: rgba(98, 72, 246, 1); + --colors-background-sentiment-solid-primary: rgba(127, 105, 255, 1); --button-blue-text: rgb(22, 119, 255); @@ -54,6 +56,7 @@ --colors-text-neutral-standard: rgba(53, 51, 65, 1); --colors-text-functional-danger: rgba(255, 81, 81, 1); --colors-text-inverse-strong: rgba(255, 255, 255, 1); + --colors-text-persist-light: rgba(255, 255, 255, 1); } .dark { @@ -123,6 +126,7 @@ --colors-background-neutral-standard: rgba(11, 10, 18, 1); --colors-background-neutral-strong: rgba(29, 26, 44, 1); --colors-background-neutral-weak: rgba(17, 16, 23, 1); + --colors-background-sentiment-solid-primary: rgba(146, 118, 255, 1); --colors-outline-sentiment-primary: rgba(146, 118, 255, 1); --colors-outline-neutral-strong: rgba(255, 255, 255, 0.15); @@ -133,6 +137,7 @@ --colors-text-neutral-standard: rgba(230, 227, 246, 1); --colors-text-functional-danger: rgba(255, 81, 81, 1); --colors-text-inverse-strong: rgba(17, 16, 23, 1); + --colors-text-persist-light: rgba(255, 255, 255, 1); } }