add functionality to export and load database

This commit is contained in:
Henry
2023-05-14 20:33:43 +01:00
parent 5d5021bf10
commit 6ab1ff1062
7 changed files with 204 additions and 24 deletions
+9
View File
@@ -0,0 +1,9 @@
import client from './client'
const getExportDatabase = () => client.get('/database/export')
const createLoadDatabase = (body) => client.post('/database/load', body)
export default {
getExportDatabase,
createLoadDatabase
}
@@ -1,8 +1,7 @@
import { useState, useRef, useEffect } from 'react'
import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'
import { useNavigate } from 'react-router-dom'
// material-ui
import { useTheme } from '@mui/material/styles'
@@ -27,9 +26,15 @@ import PerfectScrollbar from 'react-perfect-scrollbar'
// project imports
import MainCard from 'ui-component/cards/MainCard'
import Transitions from 'ui-component/extended/Transitions'
import { BackdropLoader } from 'ui-component/loading/BackdropLoader'
// assets
import { IconLogout, IconSettings } from '@tabler/icons'
import { IconLogout, IconSettings, IconFileExport, IconFileDownload } from '@tabler/icons'
// API
import databaseApi from 'api/database'
import { SET_MENU } from 'store/actions'
import './index.css'
@@ -37,12 +42,16 @@ import './index.css'
const ProfileSection = ({ username, handleLogout }) => {
const theme = useTheme()
const dispatch = useDispatch()
const navigate = useNavigate()
const customization = useSelector((state) => state.customization)
const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const anchorRef = useRef(null)
const uploadRef = useRef(null)
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
@@ -55,6 +64,56 @@ const ProfileSection = ({ username, handleLogout }) => {
setOpen((prevOpen) => !prevOpen)
}
const handleExportDB = async () => {
setOpen(false)
try {
const response = await databaseApi.getExportDatabase()
const exportItems = response.data
let dataStr = JSON.stringify(exportItems)
let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)
let exportFileDefaultName = `DB.json`
let linkElement = document.createElement('a')
linkElement.setAttribute('href', dataUri)
linkElement.setAttribute('download', exportFileDefaultName)
linkElement.click()
} 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 = async (evt) => {
if (!evt?.target?.result) {
return
}
const { result } = evt.target
if (result.includes(`"chatmessages":[`) && result.includes(`"chatflows":[`) && result.includes(`"apikeys":[`)) {
dispatch({ type: SET_MENU, opened: false })
setLoading(true)
try {
await databaseApi.createLoadDatabase(JSON.parse(result))
setLoading(false)
navigate('/', { replace: true })
navigate(0)
} catch (e) {
console.error(e)
setLoading(false)
}
} else {
alert('Incorrect Flowise Database Format')
}
}
reader.readAsText(file)
}
const prevOpen = useRef(open)
useEffect(() => {
if (prevOpen.current === true && open === false) {
@@ -109,11 +168,13 @@ const ProfileSection = ({ username, handleLogout }) => {
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>
<Box sx={{ p: 2 }}>
<Typography component='span' variant='h4'>
{username}
</Typography>
</Box>
{username && (
<Box sx={{ p: 2 }}>
<Typography component='span' variant='h4'>
{username}
</Typography>
</Box>
)}
<PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}>
<Box sx={{ p: 2 }}>
<Divider />
@@ -135,13 +196,36 @@ const ProfileSection = ({ username, handleLogout }) => {
>
<ListItemButton
sx={{ borderRadius: `${customization.borderRadius}px` }}
onClick={handleLogout}
onClick={() => {
setOpen(false)
uploadRef.current.click()
}}
>
<ListItemIcon>
<IconLogout stroke={1.5} size='1.3rem' />
<IconFileDownload stroke={1.5} size='1.3rem' />
</ListItemIcon>
<ListItemText primary={<Typography variant='body2'>Logout</Typography>} />
<ListItemText primary={<Typography variant='body2'>Load Database</Typography>} />
</ListItemButton>
<ListItemButton
sx={{ borderRadius: `${customization.borderRadius}px` }}
onClick={handleExportDB}
>
<ListItemIcon>
<IconFileExport stroke={1.5} size='1.3rem' />
</ListItemIcon>
<ListItemText primary={<Typography variant='body2'>Export Database</Typography>} />
</ListItemButton>
{localStorage.getItem('username') && localStorage.getItem('password') && (
<ListItemButton
sx={{ borderRadius: `${customization.borderRadius}px` }}
onClick={handleLogout}
>
<ListItemIcon>
<IconLogout stroke={1.5} size='1.3rem' />
</ListItemIcon>
<ListItemText primary={<Typography variant='body2'>Logout</Typography>} />
</ListItemButton>
)}
</List>
</Box>
</PerfectScrollbar>
@@ -151,6 +235,8 @@ const ProfileSection = ({ username, handleLogout }) => {
</Transitions>
)}
</Popper>
<input ref={uploadRef} type='file' hidden accept='.json' onChange={(e) => handleFileUpload(e)} />
<BackdropLoader open={loading} />
</>
)
}
@@ -127,12 +127,8 @@ const Header = ({ handleLeftDrawerToggle }) => {
</Box>
<Box sx={{ flexGrow: 1 }} />
<MaterialUISwitch checked={isDark} onChange={changeDarkMode} />
{localStorage.getItem('username') && localStorage.getItem('password') && (
<>
<Box sx={{ ml: 2 }}></Box>
<ProfileSection handleLogout={signOutClicked} username={localStorage.getItem('username') ?? 'user'} />
</>
)}
<Box sx={{ ml: 2 }}></Box>
<ProfileSection handleLogout={signOutClicked} username={localStorage.getItem('username') ?? ''} />
</>
)
}
@@ -0,0 +1,16 @@
import PropTypes from 'prop-types'
import { Backdrop, CircularProgress } from '@mui/material'
export const BackdropLoader = ({ open }) => {
return (
<div>
<Backdrop sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }} open={open}>
<CircularProgress color='inherit' />
</Backdrop>
</div>
)
}
BackdropLoader.propTypes = {
open: PropTypes.bool
}