### What problem does this PR solve? Feat: Upload agent file #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.0
| @@ -34,7 +34,7 @@ | |||
| "@radix-ui/react-slot": "^1.1.0", | |||
| "@radix-ui/react-switch": "^1.1.1", | |||
| "@radix-ui/react-tabs": "^1.1.1", | |||
| "@radix-ui/react-toast": "^1.2.2", | |||
| "@radix-ui/react-toast": "^1.2.6", | |||
| "@radix-ui/react-tooltip": "^1.1.4", | |||
| "@tailwindcss/line-clamp": "^0.4.4", | |||
| "@tanstack/react-query": "^5.40.0", | |||
| @@ -6117,22 +6117,209 @@ | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast": { | |||
| "version": "1.2.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.2.tgz", | |||
| "integrity": "sha512-Z6pqSzmAP/bFJoqMAston4eSNa+ud44NSZTiZUmUen+IOZ5nBY8kzuU5WDBVyFXPtcW6yUalOHsxM/BP6Sv8ww==", | |||
| "version": "1.2.6", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", | |||
| "integrity": "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/primitive": "1.1.0", | |||
| "@radix-ui/react-collection": "1.1.0", | |||
| "@radix-ui/react-compose-refs": "1.1.0", | |||
| "@radix-ui/primitive": "1.1.1", | |||
| "@radix-ui/react-collection": "1.1.2", | |||
| "@radix-ui/react-compose-refs": "1.1.1", | |||
| "@radix-ui/react-context": "1.1.1", | |||
| "@radix-ui/react-dismissable-layer": "1.1.1", | |||
| "@radix-ui/react-portal": "1.1.2", | |||
| "@radix-ui/react-presence": "1.1.1", | |||
| "@radix-ui/react-primitive": "2.0.0", | |||
| "@radix-ui/react-dismissable-layer": "1.1.5", | |||
| "@radix-ui/react-portal": "1.1.4", | |||
| "@radix-ui/react-presence": "1.1.2", | |||
| "@radix-ui/react-primitive": "2.0.2", | |||
| "@radix-ui/react-use-callback-ref": "1.1.0", | |||
| "@radix-ui/react-use-controllable-state": "1.1.0", | |||
| "@radix-ui/react-use-layout-effect": "1.1.0", | |||
| "@radix-ui/react-visually-hidden": "1.1.0" | |||
| "@radix-ui/react-visually-hidden": "1.1.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/primitive": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.1.tgz", | |||
| "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", | |||
| "license": "MIT" | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-collection": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", | |||
| "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-compose-refs": "1.1.1", | |||
| "@radix-ui/react-context": "1.1.1", | |||
| "@radix-ui/react-primitive": "2.0.2", | |||
| "@radix-ui/react-slot": "1.1.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-compose-refs": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", | |||
| "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-dismissable-layer": { | |||
| "version": "1.1.5", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", | |||
| "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/primitive": "1.1.1", | |||
| "@radix-ui/react-compose-refs": "1.1.1", | |||
| "@radix-ui/react-primitive": "2.0.2", | |||
| "@radix-ui/react-use-callback-ref": "1.1.0", | |||
| "@radix-ui/react-use-escape-keydown": "1.1.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-portal": { | |||
| "version": "1.1.4", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", | |||
| "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-primitive": "2.0.2", | |||
| "@radix-ui/react-use-layout-effect": "1.1.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-presence": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", | |||
| "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-compose-refs": "1.1.1", | |||
| "@radix-ui/react-use-layout-effect": "1.1.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-primitive": { | |||
| "version": "2.0.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", | |||
| "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-slot": "1.1.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-slot": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", | |||
| "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-compose-refs": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-visually-hidden": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", | |||
| "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-primitive": "2.0.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| @@ -45,7 +45,7 @@ | |||
| "@radix-ui/react-slot": "^1.1.0", | |||
| "@radix-ui/react-switch": "^1.1.1", | |||
| "@radix-ui/react-tabs": "^1.1.1", | |||
| "@radix-ui/react-toast": "^1.2.2", | |||
| "@radix-ui/react-toast": "^1.2.6", | |||
| "@radix-ui/react-tooltip": "^1.1.4", | |||
| "@tailwindcss/line-clamp": "^0.4.4", | |||
| "@tanstack/react-query": "^5.40.0", | |||
| @@ -2,11 +2,11 @@ import i18n from '@/locales/config'; | |||
| import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | |||
| import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; | |||
| import { App, ConfigProvider, ConfigProviderProps, theme } from 'antd'; | |||
| import pt_BR from 'antd/lib/locale/pt_BR'; | |||
| import enUS from 'antd/locale/en_US'; | |||
| import vi_VN from 'antd/locale/vi_VN'; | |||
| import zhCN from 'antd/locale/zh_CN'; | |||
| import zh_HK from 'antd/locale/zh_HK'; | |||
| import pt_BR from 'antd/lib/locale/pt_BR'; | |||
| import dayjs from 'dayjs'; | |||
| import advancedFormat from 'dayjs/plugin/advancedFormat'; | |||
| import customParseFormat from 'dayjs/plugin/customParseFormat'; | |||
| @@ -64,7 +64,7 @@ function Root({ children }: React.PropsWithChildren) { | |||
| }} | |||
| locale={locale} | |||
| > | |||
| <App> {children}</App> | |||
| <App>{children}</App> | |||
| </ConfigProvider> | |||
| <ReactQueryDevtools buttonPosition={'top-left'} /> | |||
| </> | |||
| @@ -3,7 +3,10 @@ | |||
| // Inspired by react-hot-toast library | |||
| import * as React from 'react'; | |||
| import type { ToastActionElement, ToastProps } from '@/components/ui/toast'; | |||
| import type { | |||
| ToastActionElement, | |||
| ToastProps, | |||
| } from '@/registry/default/ui/toast'; | |||
| const TOAST_LIMIT = 1; | |||
| const TOAST_REMOVE_DELAY = 1000000; | |||
| @@ -8,7 +8,7 @@ import { cn } from '@/lib/utils'; | |||
| import { ControllerRenderProps } from 'react-hook-form'; | |||
| import { FormControl } from '@/components/ui/form'; | |||
| import { useCallback, useEffect } from 'react'; | |||
| import { forwardRef, useCallback, useEffect } from 'react'; | |||
| const Select = SelectPrimitive.Root; | |||
| @@ -20,8 +20,9 @@ const SelectTrigger = React.forwardRef< | |||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | |||
| React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & { | |||
| onReset?: () => void; | |||
| allowClear?: boolean; | |||
| } | |||
| >(({ className, children, value, onReset, ...props }, ref) => ( | |||
| >(({ className, children, value, onReset, allowClear, ...props }, ref) => ( | |||
| <SelectPrimitive.Trigger | |||
| ref={ref} | |||
| className={cn( | |||
| @@ -37,7 +38,7 @@ const SelectTrigger = React.forwardRef< | |||
| event.stopPropagation(); | |||
| }} | |||
| > | |||
| {value ? ( | |||
| {value && allowClear ? ( | |||
| <X className="h-4 w-4 opacity-50 cursor-pointer" onClick={onReset} /> | |||
| ) : ( | |||
| <ChevronDown className="h-4 w-4 opacity-50" /> | |||
| @@ -188,6 +189,7 @@ export type RAGFlowSelectGroupOptionType = { | |||
| type RAGFlowSelectProps = Partial<ControllerRenderProps> & { | |||
| FormControlComponent?: typeof FormControl; | |||
| options?: (RAGFlowSelectOptionType | RAGFlowSelectGroupOptionType)[]; | |||
| allowClear?: boolean; | |||
| }; | |||
| /** | |||
| @@ -206,18 +208,25 @@ type RAGFlowSelectProps = Partial<ControllerRenderProps> & { | |||
| * } | |||
| * @return {*} | |||
| */ | |||
| export function RAGFlowSelect({ | |||
| value: initialValue, | |||
| onChange, | |||
| FormControlComponent, | |||
| options = [], | |||
| }: RAGFlowSelectProps) { | |||
| export const RAGFlowSelect = forwardRef< | |||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | |||
| RAGFlowSelectProps | |||
| >(function ( | |||
| { | |||
| value: initialValue, | |||
| onChange, | |||
| FormControlComponent, | |||
| options = [], | |||
| allowClear, | |||
| }, | |||
| ref, | |||
| ) { | |||
| const [key, setKey] = React.useState(+new Date()); | |||
| const [value, setValue] = React.useState<string | undefined>(undefined); | |||
| const FormControlWidget = FormControlComponent | |||
| ? FormControlComponent | |||
| : React.Fragment; | |||
| : ({ children }: React.PropsWithChildren) => <div>{children}</div>; | |||
| const handleChange = useCallback( | |||
| (val?: string) => { | |||
| @@ -248,6 +257,8 @@ export function RAGFlowSelect({ | |||
| className="bg-colors-background-inverse-weak" | |||
| value={value} | |||
| onReset={handleReset} | |||
| allowClear={allowClear} | |||
| ref={ref} | |||
| > | |||
| <SelectValue placeholder="Select a verified email to display" /> | |||
| </SelectTrigger> | |||
| @@ -280,4 +291,4 @@ export function RAGFlowSelect({ | |||
| </SelectContent> | |||
| </Select> | |||
| ); | |||
| } | |||
| }); | |||
| @@ -25,7 +25,7 @@ const ToastViewport = React.forwardRef< | |||
| ToastViewport.displayName = ToastPrimitives.Viewport.displayName; | |||
| const toastVariants = cva( | |||
| 'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full', | |||
| 'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full', | |||
| { | |||
| variants: { | |||
| variant: { | |||
| @@ -62,7 +62,7 @@ const ToastAction = React.forwardRef< | |||
| <ToastPrimitives.Action | |||
| ref={ref} | |||
| className={cn( | |||
| 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive', | |||
| 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive', | |||
| className, | |||
| )} | |||
| {...props} | |||
| @@ -77,7 +77,7 @@ const ToastClose = React.forwardRef< | |||
| <ToastPrimitives.Close | |||
| ref={ref} | |||
| className={cn( | |||
| 'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600', | |||
| 'absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600', | |||
| className, | |||
| )} | |||
| toast-close="" | |||
| @@ -94,7 +94,7 @@ const ToastTitle = React.forwardRef< | |||
| >(({ className, ...props }, ref) => ( | |||
| <ToastPrimitives.Title | |||
| ref={ref} | |||
| className={cn('text-sm font-semibold', className)} | |||
| className={cn('text-sm font-semibold [&+div]:text-xs', className)} | |||
| {...props} | |||
| /> | |||
| )); | |||
| @@ -15,7 +15,7 @@ export function Toaster() { | |||
| return ( | |||
| <ToastProvider> | |||
| {toasts.map(function ({ id, title, description, action, ...props }: any) { | |||
| {toasts.map(function ({ id, title, description, action, ...props }) { | |||
| return ( | |||
| <Toast key={id} {...props}> | |||
| <div className="grid gap-1"> | |||
| @@ -138,3 +138,10 @@ export const ExceptiveType = ['xlsx', 'xls', 'pdf', 'docx', ...Images]; | |||
| export const SupportedPreviewDocumentTypes = [...ExceptiveType]; | |||
| //#endregion | |||
| export enum Platform { | |||
| RAGFlow = 'RAGFlow', | |||
| Dify = 'Dify', | |||
| FastGPT = 'FastGPT', | |||
| Coze = 'Coze', | |||
| } | |||
| @@ -1,9 +1,10 @@ | |||
| import { FileMimeType } from '@/constants/common'; | |||
| import { useToast } from '@/components/hooks/use-toast'; | |||
| import { FileMimeType, Platform } from '@/constants/common'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { useFetchFlow } from '@/hooks/flow-hooks'; | |||
| import { IGraph } from '@/interfaces/database/flow'; | |||
| import { downloadJsonFile } from '@/utils/file-util'; | |||
| import { message, UploadFile } from 'antd'; | |||
| import { message } from 'antd'; | |||
| import isEmpty from 'lodash/isEmpty'; | |||
| import { useCallback } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| @@ -20,13 +21,21 @@ export const useHandleExportOrImportJsonFile = () => { | |||
| const setGraphInfo = useSetGraphInfo(); | |||
| const { data } = useFetchFlow(); | |||
| const { t } = useTranslation(); | |||
| const { toast } = useToast(); | |||
| const onFileUploadOk = useCallback( | |||
| async (fileList: UploadFile[]) => { | |||
| async ({ | |||
| fileList, | |||
| platform, | |||
| }: { | |||
| fileList: File[]; | |||
| platform: Platform; | |||
| }) => { | |||
| console.log('🚀 ~ useHandleExportOrImportJsonFile ~ platform:', platform); | |||
| if (fileList.length > 0) { | |||
| const file: File = fileList[0] as unknown as File; | |||
| const file = fileList[0]; | |||
| if (file.type !== FileMimeType.Json) { | |||
| message.error(t('flow.jsonUploadTypeErrorMessage')); | |||
| toast({ title: t('flow.jsonUploadTypeErrorMessage') }); | |||
| return; | |||
| } | |||
| @@ -45,7 +54,7 @@ export const useHandleExportOrImportJsonFile = () => { | |||
| } | |||
| } | |||
| }, | |||
| [hideFileUploadModal, setGraphInfo, t], | |||
| [hideFileUploadModal, setGraphInfo, t, toast], | |||
| ); | |||
| const handleExportJson = useCallback(() => { | |||
| @@ -1,12 +1,35 @@ | |||
| import { PageHeader } from '@/components/page-header'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| DropdownMenu, | |||
| DropdownMenuContent, | |||
| DropdownMenuItem, | |||
| DropdownMenuSeparator, | |||
| DropdownMenuTrigger, | |||
| } from '@/components/ui/dropdown-menu'; | |||
| import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { Trash2 } from 'lucide-react'; | |||
| import { CodeXml, EllipsisVertical, Forward, Import, Key } from 'lucide-react'; | |||
| import { ComponentPropsWithoutRef } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { AgentSidebar } from './agent-sidebar'; | |||
| import FlowCanvas from './canvas'; | |||
| import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; | |||
| import { useFetchDataOnMount } from './hooks/use-fetch-data'; | |||
| import { useOpenDocument } from './hooks/use-open-document'; | |||
| import { UploadAgentDialog } from './upload-agent-dialog'; | |||
| function AgentDropdownMenuItem({ | |||
| children, | |||
| ...props | |||
| }: ComponentPropsWithoutRef<typeof DropdownMenuItem>) { | |||
| return ( | |||
| <DropdownMenuItem className="flex justify-between items-center" {...props}> | |||
| {children} | |||
| </DropdownMenuItem> | |||
| ); | |||
| } | |||
| export default function Agent() { | |||
| const { navigateToAgentList } = useNavigatePage(); | |||
| @@ -15,6 +38,15 @@ export default function Agent() { | |||
| hideModal: hideChatDrawer, | |||
| showModal: showChatDrawer, | |||
| } = useSetModalState(); | |||
| const { t } = useTranslation(); | |||
| const openDocument = useOpenDocument(); | |||
| const { | |||
| handleExportJson, | |||
| handleImportJson, | |||
| fileUploadVisible, | |||
| onFileUploadOk, | |||
| hideFileUploadModal, | |||
| } = useHandleExportOrImportJsonFile(); | |||
| useFetchDataOnMount(); | |||
| @@ -22,15 +54,38 @@ export default function Agent() { | |||
| <section> | |||
| <PageHeader back={navigateToAgentList} title="Agent 01"> | |||
| <div className="flex items-center gap-2"> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <Trash2 /> | |||
| </Button> | |||
| <DropdownMenu> | |||
| <DropdownMenuTrigger> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <EllipsisVertical /> | |||
| </Button> | |||
| </DropdownMenuTrigger> | |||
| <DropdownMenuContent> | |||
| <AgentDropdownMenuItem onClick={openDocument}> | |||
| API | |||
| <Key /> | |||
| </AgentDropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <AgentDropdownMenuItem onClick={handleImportJson}> | |||
| Import | |||
| <Import /> | |||
| </AgentDropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <AgentDropdownMenuItem onClick={handleExportJson}> | |||
| Export | |||
| <Forward /> | |||
| </AgentDropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <AgentDropdownMenuItem> | |||
| {t('common.embedIntoSite')} | |||
| <CodeXml /> | |||
| </AgentDropdownMenuItem> | |||
| </DropdownMenuContent> | |||
| </DropdownMenu> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| Save | |||
| </Button> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| API | |||
| </Button> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| Run app | |||
| </Button> | |||
| @@ -54,6 +109,12 @@ export default function Agent() { | |||
| </div> | |||
| </SidebarProvider> | |||
| </div> | |||
| {fileUploadVisible && ( | |||
| <UploadAgentDialog | |||
| hideModal={hideFileUploadModal} | |||
| onOk={onFileUploadOk} | |||
| ></UploadAgentDialog> | |||
| )} | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| import { | |||
| Dialog, | |||
| DialogContent, | |||
| DialogFooter, | |||
| DialogHeader, | |||
| DialogTitle, | |||
| } from '@/components/ui/dialog'; | |||
| import { LoadingButton } from '@/components/ui/loading-button'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { TagRenameId } from '@/pages/add-knowledge/constant'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { UploadAgentForm } from './upload-agent-form'; | |||
| export function UploadAgentDialog({ | |||
| hideModal, | |||
| onOk, | |||
| loading, | |||
| }: IModalProps<any>) { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <Dialog open onOpenChange={hideModal}> | |||
| <DialogContent className="sm:max-w-[425px]"> | |||
| <DialogHeader> | |||
| <DialogTitle>{t('fileManager.uploadFile')}</DialogTitle> | |||
| </DialogHeader> | |||
| <UploadAgentForm hideModal={hideModal} onOk={onOk}></UploadAgentForm> | |||
| <DialogFooter> | |||
| <LoadingButton type="submit" form={TagRenameId} loading={loading}> | |||
| {t('common.save')} | |||
| </LoadingButton> | |||
| </DialogFooter> | |||
| </DialogContent> | |||
| </Dialog> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,91 @@ | |||
| 'use client'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| import { FileUploader } from '@/components/file-uploader'; | |||
| import { | |||
| Form, | |||
| FormControl, | |||
| FormField, | |||
| FormItem, | |||
| FormLabel, | |||
| FormMessage, | |||
| } from '@/components/ui/form'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { FileMimeType, Platform } from '@/constants/common'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { TagRenameId } from '@/pages/add-knowledge/constant'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| const options = Object.values(Platform).map((x) => ({ label: x, value: x })); | |||
| export function UploadAgentForm({ hideModal, onOk }: IModalProps<any>) { | |||
| const { t } = useTranslation(); | |||
| const FormSchema = z.object({ | |||
| platform: z | |||
| .string() | |||
| .min(1, { | |||
| message: t('common.namePlaceholder'), | |||
| }) | |||
| .trim(), | |||
| fileList: z.array(z.instanceof(File)), | |||
| }); | |||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||
| resolver: zodResolver(FormSchema), | |||
| defaultValues: { platform: Platform.RAGFlow }, | |||
| }); | |||
| async function onSubmit(data: z.infer<typeof FormSchema>) { | |||
| console.log('🚀 ~ onSubmit ~ data:', data); | |||
| const ret = await onOk?.(data); | |||
| if (ret) { | |||
| hideModal?.(); | |||
| } | |||
| } | |||
| return ( | |||
| <Form {...form}> | |||
| <form | |||
| onSubmit={form.handleSubmit(onSubmit)} | |||
| className="space-y-6" | |||
| id={TagRenameId} | |||
| > | |||
| <FormField | |||
| control={form.control} | |||
| name="fileList" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('common.name')}</FormLabel> | |||
| <FormControl> | |||
| <FileUploader | |||
| value={field.value} | |||
| onValueChange={field.onChange} | |||
| maxFileCount={1} | |||
| maxSize={4 * 1024 * 1024} | |||
| accept={{ '*.json': [FileMimeType.Json] }} | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name="platform" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('common.name')}</FormLabel> | |||
| <FormControl> | |||
| <RAGFlowSelect {...field} options={options} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| </form> | |||
| </Form> | |||
| ); | |||
| } | |||