285 lines
11 KiB
JavaScript
285 lines
11 KiB
JavaScript
import { useEffect, useState, useRef } from 'react'
|
|
|
|
// material-ui
|
|
import { Box, Stack, ButtonGroup, Skeleton, ToggleButtonGroup, ToggleButton } from '@mui/material'
|
|
import { useTheme } from '@mui/material/styles'
|
|
|
|
// project imports
|
|
import MainCard from '@/ui-component/cards/MainCard'
|
|
import ItemCard from '@/ui-component/cards/ItemCard'
|
|
import ToolDialog from './ToolDialog'
|
|
import ViewHeader from '@/layout/MainLayout/ViewHeader'
|
|
import ErrorBoundary from '@/ErrorBoundary'
|
|
import { ToolsTable } from '@/ui-component/table/ToolsListTable'
|
|
import { PermissionButton, StyledPermissionButton } from '@/ui-component/button/RBACButtons'
|
|
import TablePagination, { DEFAULT_ITEMS_PER_PAGE } from '@/ui-component/pagination/TablePagination'
|
|
|
|
// API
|
|
import toolsApi from '@/api/tools'
|
|
|
|
// Hooks
|
|
import useApi from '@/hooks/useApi'
|
|
import { useError } from '@/store/context/ErrorContext'
|
|
import { gridSpacing } from '@/store/constant'
|
|
|
|
// icons
|
|
import { IconPlus, IconFileUpload, IconLayoutGrid, IconList } from '@tabler/icons-react'
|
|
import ToolEmptySVG from '@/assets/images/tools_empty.svg'
|
|
|
|
// ==============================|| TOOLS ||============================== //
|
|
|
|
const Tools = () => {
|
|
const theme = useTheme()
|
|
const getAllToolsApi = useApi(toolsApi.getAllTools)
|
|
const { error, setError } = useError()
|
|
|
|
const [isLoading, setLoading] = useState(true)
|
|
const [showDialog, setShowDialog] = useState(false)
|
|
const [dialogProps, setDialogProps] = useState({})
|
|
const [view, setView] = useState(localStorage.getItem('toolsDisplayStyle') || 'card')
|
|
|
|
const inputRef = useRef(null)
|
|
|
|
/* Table Pagination */
|
|
const [currentPage, setCurrentPage] = useState(1)
|
|
const [pageLimit, setPageLimit] = useState(DEFAULT_ITEMS_PER_PAGE)
|
|
const [total, setTotal] = useState(0)
|
|
|
|
const onChange = (page, pageLimit) => {
|
|
setCurrentPage(page)
|
|
setPageLimit(pageLimit)
|
|
refresh(page, pageLimit)
|
|
}
|
|
|
|
const refresh = (page, limit) => {
|
|
const params = {
|
|
page: page || currentPage,
|
|
limit: limit || pageLimit
|
|
}
|
|
getAllToolsApi.request(params)
|
|
}
|
|
|
|
const handleChange = (event, nextView) => {
|
|
if (nextView === null) return
|
|
localStorage.setItem('toolsDisplayStyle', nextView)
|
|
setView(nextView)
|
|
}
|
|
|
|
const onUploadFile = (file) => {
|
|
try {
|
|
const dialogProp = {
|
|
title: 'Add New Tool',
|
|
type: 'IMPORT',
|
|
cancelButtonName: 'Cancel',
|
|
confirmButtonName: 'Save',
|
|
data: JSON.parse(file)
|
|
}
|
|
setDialogProps(dialogProp)
|
|
setShowDialog(true)
|
|
} catch (e) {
|
|
console.error(e)
|
|
}
|
|
}
|
|
|
|
const handleFileUpload = (e) => {
|
|
if (!e.target.files) return
|
|
|
|
const file = e.target.files[0]
|
|
|
|
const reader = new FileReader()
|
|
reader.onload = (evt) => {
|
|
if (!evt?.target?.result) {
|
|
return
|
|
}
|
|
const { result } = evt.target
|
|
onUploadFile(result)
|
|
}
|
|
reader.readAsText(file)
|
|
}
|
|
|
|
const addNew = () => {
|
|
const dialogProp = {
|
|
title: 'Add New Tool',
|
|
type: 'ADD',
|
|
cancelButtonName: 'Cancel',
|
|
confirmButtonName: 'Add'
|
|
}
|
|
setDialogProps(dialogProp)
|
|
setShowDialog(true)
|
|
}
|
|
|
|
const edit = (selectedTool) => {
|
|
const dialogProp = {
|
|
title: 'Edit Tool',
|
|
type: 'EDIT',
|
|
cancelButtonName: 'Cancel',
|
|
confirmButtonName: 'Save',
|
|
data: selectedTool
|
|
}
|
|
setDialogProps(dialogProp)
|
|
setShowDialog(true)
|
|
}
|
|
|
|
const onConfirm = () => {
|
|
setShowDialog(false)
|
|
refresh(currentPage, pageLimit)
|
|
}
|
|
|
|
const [search, setSearch] = useState('')
|
|
const onSearchChange = (event) => {
|
|
setSearch(event.target.value)
|
|
}
|
|
|
|
function filterTools(data) {
|
|
return (
|
|
data.name.toLowerCase().indexOf(search.toLowerCase()) > -1 || data.description.toLowerCase().indexOf(search.toLowerCase()) > -1
|
|
)
|
|
}
|
|
|
|
useEffect(() => {
|
|
refresh(currentPage, pageLimit)
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
setLoading(getAllToolsApi.loading)
|
|
}, [getAllToolsApi.loading])
|
|
|
|
useEffect(() => {
|
|
if (getAllToolsApi.data) {
|
|
setTotal(getAllToolsApi.data.total)
|
|
}
|
|
}, [getAllToolsApi.data])
|
|
|
|
return (
|
|
<>
|
|
<MainCard>
|
|
{error ? (
|
|
<ErrorBoundary error={error} />
|
|
) : (
|
|
<Stack flexDirection='column' sx={{ gap: 3 }}>
|
|
<ViewHeader
|
|
onSearchChange={onSearchChange}
|
|
search={true}
|
|
searchPlaceholder='Search Tools'
|
|
title='Tools'
|
|
description='External functions or APIs the agent can use to take action'
|
|
>
|
|
<ToggleButtonGroup
|
|
sx={{ borderRadius: 2, maxHeight: 40 }}
|
|
value={view}
|
|
color='primary'
|
|
disabled={total === 0}
|
|
exclusive
|
|
onChange={handleChange}
|
|
>
|
|
<ToggleButton
|
|
sx={{
|
|
borderColor: theme.palette.grey[900] + 25,
|
|
borderRadius: 2,
|
|
color: theme?.customization?.isDarkMode ? 'white' : 'inherit'
|
|
}}
|
|
variant='contained'
|
|
value='card'
|
|
title='Card View'
|
|
>
|
|
<IconLayoutGrid />
|
|
</ToggleButton>
|
|
<ToggleButton
|
|
sx={{
|
|
borderColor: theme.palette.grey[900] + 25,
|
|
borderRadius: 2,
|
|
color: theme?.customization?.isDarkMode ? 'white' : 'inherit'
|
|
}}
|
|
variant='contained'
|
|
value='list'
|
|
title='List View'
|
|
>
|
|
<IconList />
|
|
</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<PermissionButton
|
|
permissionId={'tools:create'}
|
|
variant='outlined'
|
|
onClick={() => inputRef.current.click()}
|
|
startIcon={<IconFileUpload />}
|
|
sx={{ borderRadius: 2, height: 40 }}
|
|
>
|
|
Load
|
|
</PermissionButton>
|
|
<input
|
|
style={{ display: 'none' }}
|
|
ref={inputRef}
|
|
type='file'
|
|
hidden
|
|
accept='.json'
|
|
onChange={(e) => handleFileUpload(e)}
|
|
/>
|
|
</Box>
|
|
<ButtonGroup disableElevation aria-label='outlined primary button group'>
|
|
<StyledPermissionButton
|
|
permissionId={'tools:create'}
|
|
variant='contained'
|
|
onClick={addNew}
|
|
startIcon={<IconPlus />}
|
|
sx={{ borderRadius: 2, height: 40 }}
|
|
>
|
|
Create
|
|
</StyledPermissionButton>
|
|
</ButtonGroup>
|
|
</ViewHeader>
|
|
{isLoading && (
|
|
<Box display='grid' gridTemplateColumns='repeat(3, 1fr)' gap={gridSpacing}>
|
|
<Skeleton variant='rounded' height={160} />
|
|
<Skeleton variant='rounded' height={160} />
|
|
<Skeleton variant='rounded' height={160} />
|
|
</Box>
|
|
)}
|
|
{!isLoading && total > 0 && (
|
|
<>
|
|
{!view || view === 'card' ? (
|
|
<Box display='grid' gridTemplateColumns='repeat(3, 1fr)' gap={gridSpacing}>
|
|
{getAllToolsApi.data?.data?.filter(filterTools).map((data, index) => (
|
|
<ItemCard data={data} key={index} onClick={() => edit(data)} />
|
|
))}
|
|
</Box>
|
|
) : (
|
|
<ToolsTable
|
|
data={getAllToolsApi.data?.data?.filter(filterTools) || []}
|
|
isLoading={isLoading}
|
|
onSelect={edit}
|
|
/>
|
|
)}
|
|
{/* Pagination and Page Size Controls */}
|
|
<TablePagination currentPage={currentPage} limit={pageLimit} total={total} onChange={onChange} />
|
|
</>
|
|
)}
|
|
{!isLoading && total === 0 && (
|
|
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
|
<Box sx={{ p: 2, height: 'auto' }}>
|
|
<img
|
|
style={{ objectFit: 'cover', height: '20vh', width: 'auto' }}
|
|
src={ToolEmptySVG}
|
|
alt='ToolEmptySVG'
|
|
/>
|
|
</Box>
|
|
<div>No Tools Created Yet</div>
|
|
</Stack>
|
|
)}
|
|
</Stack>
|
|
)}
|
|
</MainCard>
|
|
<ToolDialog
|
|
show={showDialog}
|
|
dialogProps={dialogProps}
|
|
onCancel={() => setShowDialog(false)}
|
|
onConfirm={onConfirm}
|
|
setError={setError}
|
|
></ToolDialog>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Tools
|